├── .github └── workflows │ ├── release-docker.yaml │ └── release-ipk.yaml ├── .gitignore ├── AUTHORS ├── COPYING ├── ChangeLog ├── Dockerfile ├── INSTALL ├── Makefile.am ├── NEWS ├── README ├── README.md ├── configure.ac ├── openwrt-support ├── luci-app-rtp2httpd │ ├── Makefile │ ├── htdocs │ │ └── luci-static │ │ │ └── resources │ │ │ └── view │ │ │ └── rtp2httpd.js │ ├── po │ │ ├── en │ │ │ └── rtp2httpd.po │ │ └── zh_Hans │ │ │ └── rtp2httpd.po │ └── root │ │ └── usr │ │ └── share │ │ ├── luci │ │ └── menu.d │ │ │ └── luci-app-rtp2httpd.json │ │ └── rpcd │ │ └── acl.d │ │ └── luci-app-rtp2httpd.json └── rtp2httpd │ ├── Makefile │ └── files │ ├── rtp2httpd.conf │ └── rtp2httpd.init ├── rtp2httpd.conf └── src ├── Makefile.am ├── configuration.c ├── httpclients.c ├── rtp2httpd.c └── rtp2httpd.h /.github/workflows/release-docker.yaml: -------------------------------------------------------------------------------- 1 | name: Docker Release 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: read 13 | packages: write 14 | 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@v4 18 | 19 | - name: Set up Docker Buildx 20 | uses: docker/setup-buildx-action@v3 21 | 22 | - name: Log in to GitHub Container Registry 23 | uses: docker/login-action@v3 24 | with: 25 | registry: ghcr.io 26 | username: ${{ github.actor }} 27 | password: ${{ secrets.GITHUB_TOKEN }} 28 | 29 | - name: Extract metadata for Docker 30 | id: meta 31 | uses: docker/metadata-action@v5 32 | with: 33 | images: ghcr.io/${{ github.repository }} 34 | tags: | 35 | type=semver,pattern={{version}} 36 | type=semver,pattern={{major}}.{{minor}} 37 | type=semver,pattern={{major}} 38 | type=raw,value=latest,enable={{is_default_branch}} 39 | 40 | - name: Build and push Docker image 41 | uses: docker/build-push-action@v6 42 | with: 43 | context: . 44 | platforms: linux/amd64,linux/arm64,linux/arm/v7 45 | push: true 46 | tags: ${{ steps.meta.outputs.tags }} 47 | labels: ${{ steps.meta.outputs.labels }} 48 | cache-from: type=gha 49 | cache-to: type=gha,mode=max 50 | -------------------------------------------------------------------------------- /.github/workflows/release-ipk.yaml: -------------------------------------------------------------------------------- 1 | name: IPK Release 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | jobs: 9 | build: 10 | name: ${{ matrix.arch }}-${{ matrix.sdk }} 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: write 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | arch: 18 | - x86_64 19 | - aarch64_cortex-a53 20 | - aarch64_cortex-a72 21 | - aarch64_generic 22 | - arm_arm1176jzf-s_vfp 23 | - arm_arm926ej-s 24 | - arm_cortex-a15_neon-vfpv4 25 | - arm_cortex-a5_vfpv4 26 | - arm_cortex-a7 27 | - arm_cortex-a7_neon-vfpv4 28 | - arm_cortex-a8_vfpv3 29 | - arm_cortex-a9 30 | - arm_cortex-a9_neon 31 | - arm_cortex-a9_vfpv3-d16 32 | - arm_fa526 33 | - arm_mpcore 34 | - arm_xscale 35 | - i386_pentium-mmx 36 | - i386_pentium4 37 | - mips64_octeonplus 38 | - mips_24kc 39 | - mips_4kec 40 | - mips_mips32 41 | - mipsel_24kc 42 | - mipsel_24kc_24kf 43 | - mipsel_74kc 44 | - mipsel_mips32 45 | sdk: 46 | - "22.03.6" 47 | 48 | steps: 49 | - uses: actions/checkout@v4 50 | 51 | - name: Override version number 52 | run: | 53 | RELEASE_TAG="${{ github.event.release.tag_name }}" 54 | RELEASE_VERSION="${RELEASE_TAG#v}" 55 | sed -i "s/1\.0\.0/${RELEASE_VERSION}/g" openwrt-support/rtp2httpd/Makefile openwrt-support/luci-app-rtp2httpd/Makefile configure.ac 56 | 57 | - name: Move src to openwrt-support 58 | run: mv openwrt-support ../ && mkdir ../openwrt-support/rtp2httpd/src && mv ./* ../openwrt-support/rtp2httpd/src && mv ../openwrt-support ./ 59 | 60 | - name: Build 61 | uses: openwrt/gh-action-sdk@v7 62 | env: 63 | ARCH: ${{ matrix.arch }}-${{ matrix.sdk }} 64 | FEED_DIR: ${{ github.workspace }}/openwrt-support 65 | NO_SHFMT_CHECK: true 66 | PACKAGES: ${{ matrix.arch == 'x86_64' && 'luci-app-rtp2httpd' || 'rtp2httpd' }} 67 | 68 | - name: Upload to release assets 69 | run: gh release upload --repo ${{ github.repository }} ${{ github.event.release.tag_name }} bin/packages/${{ matrix.arch }}/action/*.ipk 70 | env: 71 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | rtp2httpd 3 | !openwrt-support/rtp2httpd 4 | !openwrt-support/rtp2httpd/Makefile 5 | !openwrt-support/luci-app-rtp2httpd/Makefile 6 | *.o 7 | *.in 8 | config.* 9 | stamp-h1 10 | .deps 11 | autom4te.cache 12 | aclocal.m4 13 | compile 14 | configure 15 | configure~ 16 | depcomp 17 | install-sh 18 | missing 19 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Written by: 2 | Ondrej Caletka 3 | 4 | MUDP Support by: 5 | Richard Golier 6 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2010-05-26 16:00 Ondrej Caletka 2 | Version 0.4 3 | Fixed zombie processes creation when more than one 4 | client disconnects in a short period of time. 5 | Fix by David Seidl 6 | 7 | 2009-04-05 23:30 Ondrej Caletka 8 | Version 0.3 9 | Added support for RAW UDP (MUDP) streams by 10 | Richard Golier 11 | 12 | 2009-01-16 10:00 Ondrej Caletka 13 | Version 0.2b 14 | Fixed reporting errors od getaddrinfo(); 15 | 16 | 2009-01-08 16:00 Ondrej Caletka 17 | Version 0.2a 18 | Fixed fatal error in writeToClient(); 19 | 20 | 2009-01-08 11:00 Ondrej Caletka 21 | Version 0.2 22 | Added -D option to force not to daemonise. 23 | Changed configfile read order so configfile is 24 | parsed before parsing command-line args. 25 | 26 | 2009-01-07 23:00 Ondrej Caletka 27 | Fixes address in use when two clients wants same 28 | source mRTP stream. 29 | 30 | 2009-01-07 17:20 Ondrej Caletka 31 | Initial release. 32 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Build stage 2 | FROM --platform=$BUILDPLATFORM debian:bullseye-slim AS builder 3 | 4 | ARG TARGETPLATFORM 5 | ARG BUILDPLATFORM 6 | 7 | # Install build dependencies 8 | RUN set -ex; \ 9 | apt-get update; \ 10 | apt-get install -y \ 11 | autoconf \ 12 | automake \ 13 | pkg-config; \ 14 | case "$TARGETPLATFORM" in \ 15 | "$BUILDPLATFORM") apt-get install -y build-essential ;; \ 16 | "linux/arm64") \ 17 | dpkg --add-architecture arm64 && \ 18 | apt-get install -y \ 19 | crossbuild-essential-arm64 \ 20 | gcc-aarch64-linux-gnu ;; \ 21 | "linux/arm/v7") \ 22 | dpkg --add-architecture armhf && \ 23 | apt-get install -y \ 24 | crossbuild-essential-armhf \ 25 | gcc-arm-linux-gnueabihf ;; \ 26 | "linux/amd64") \ 27 | dpkg --add-architecture amd64 && \ 28 | apt-get install -y \ 29 | crossbuild-essential-amd64 \ 30 | gcc-x86-64-linux-gnu ;; \ 31 | *) echo "Unsupported platform combination: $BUILDPLATFORM -> $TARGETPLATFORM" && exit 1 ;; \ 32 | esac && \ 33 | rm -rf /var/lib/apt/lists/* 34 | 35 | # Copy source code 36 | WORKDIR /workdir 37 | COPY . . 38 | 39 | RUN case "$TARGETPLATFORM" in \ 40 | "$BUILDPLATFORM") ARCH_FLAGS="" ;; \ 41 | "linux/amd64") ARCH_FLAGS="--host=x86_64-linux-gnu" ;; \ 42 | "linux/arm64") ARCH_FLAGS="--host=aarch64-linux-gnu" ;; \ 43 | "linux/arm/v7") ARCH_FLAGS="--host=arm-linux-gnueabihf" ;; \ 44 | esac && \ 45 | echo "Building with ARCH_FLAGS=$ARCH_FLAGS" && \ 46 | autoreconf -fi && \ 47 | ./configure ${ARCH_FLAGS} && \ 48 | make 49 | 50 | # Runtime stage 51 | FROM debian:bullseye-slim 52 | 53 | # Install runtime dependencies 54 | RUN apt-get update && apt-get install -y \ 55 | libgcc1 \ 56 | && rm -rf /var/lib/apt/lists/* 57 | 58 | # Copy the built binary and config 59 | COPY --from=builder /workdir/src/rtp2httpd /usr/local/bin/ 60 | COPY --from=builder /workdir/rtp2httpd.conf /usr/local/etc/ 61 | 62 | # Expose the default port 63 | EXPOSE 8080 64 | 65 | # Run the application 66 | ENTRYPOINT ["/usr/local/bin/rtp2httpd"] 67 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Installation Instructions 2 | ************************* 3 | 4 | Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, 5 | 2006, 2007, 2008, 2009 Free Software Foundation, Inc. 6 | 7 | Copying and distribution of this file, with or without modification, 8 | are permitted in any medium without royalty provided the copyright 9 | notice and this notice are preserved. This file is offered as-is, 10 | without warranty of any kind. 11 | 12 | Basic Installation 13 | ================== 14 | 15 | Briefly, the shell commands `./configure; make; make install' should 16 | configure, build, and install this package. The following 17 | more-detailed instructions are generic; see the `README' file for 18 | instructions specific to this package. Some packages provide this 19 | `INSTALL' file but do not implement all of the features documented 20 | below. The lack of an optional feature in a given package is not 21 | necessarily a bug. More recommendations for GNU packages can be found 22 | in *note Makefile Conventions: (standards)Makefile Conventions. 23 | 24 | The `configure' shell script attempts to guess correct values for 25 | various system-dependent variables used during compilation. It uses 26 | those values to create a `Makefile' in each directory of the package. 27 | It may also create one or more `.h' files containing system-dependent 28 | definitions. Finally, it creates a shell script `config.status' that 29 | you can run in the future to recreate the current configuration, and a 30 | file `config.log' containing compiler output (useful mainly for 31 | debugging `configure'). 32 | 33 | It can also use an optional file (typically called `config.cache' 34 | and enabled with `--cache-file=config.cache' or simply `-C') that saves 35 | the results of its tests to speed up reconfiguring. Caching is 36 | disabled by default to prevent problems with accidental use of stale 37 | cache files. 38 | 39 | If you need to do unusual things to compile the package, please try 40 | to figure out how `configure' could check whether to do them, and mail 41 | diffs or instructions to the address given in the `README' so they can 42 | be considered for the next release. If you are using the cache, and at 43 | some point `config.cache' contains results you don't want to keep, you 44 | may remove or edit it. 45 | 46 | The file `configure.ac' (or `configure.in') is used to create 47 | `configure' by a program called `autoconf'. You need `configure.ac' if 48 | you want to change it or regenerate `configure' using a newer version 49 | of `autoconf'. 50 | 51 | The simplest way to compile this package is: 52 | 53 | 1. `cd' to the directory containing the package's source code and type 54 | `./configure' to configure the package for your system. 55 | 56 | Running `configure' might take a while. While running, it prints 57 | some messages telling which features it is checking for. 58 | 59 | 2. Type `make' to compile the package. 60 | 61 | 3. Optionally, type `make check' to run any self-tests that come with 62 | the package, generally using the just-built uninstalled binaries. 63 | 64 | 4. Type `make install' to install the programs and any data files and 65 | documentation. When installing into a prefix owned by root, it is 66 | recommended that the package be configured and built as a regular 67 | user, and only the `make install' phase executed with root 68 | privileges. 69 | 70 | 5. Optionally, type `make installcheck' to repeat any self-tests, but 71 | this time using the binaries in their final installed location. 72 | This target does not install anything. Running this target as a 73 | regular user, particularly if the prior `make install' required 74 | root privileges, verifies that the installation completed 75 | correctly. 76 | 77 | 6. You can remove the program binaries and object files from the 78 | source code directory by typing `make clean'. To also remove the 79 | files that `configure' created (so you can compile the package for 80 | a different kind of computer), type `make distclean'. There is 81 | also a `make maintainer-clean' target, but that is intended mainly 82 | for the package's developers. If you use it, you may have to get 83 | all sorts of other programs in order to regenerate files that came 84 | with the distribution. 85 | 86 | 7. Often, you can also type `make uninstall' to remove the installed 87 | files again. In practice, not all packages have tested that 88 | uninstallation works correctly, even though it is required by the 89 | GNU Coding Standards. 90 | 91 | 8. Some packages, particularly those that use Automake, provide `make 92 | distcheck', which can by used by developers to test that all other 93 | targets like `make install' and `make uninstall' work correctly. 94 | This target is generally not run by end users. 95 | 96 | Compilers and Options 97 | ===================== 98 | 99 | Some systems require unusual options for compilation or linking that 100 | the `configure' script does not know about. Run `./configure --help' 101 | for details on some of the pertinent environment variables. 102 | 103 | You can give `configure' initial values for configuration parameters 104 | by setting variables in the command line or in the environment. Here 105 | is an example: 106 | 107 | ./configure CC=c99 CFLAGS=-g LIBS=-lposix 108 | 109 | *Note Defining Variables::, for more details. 110 | 111 | Compiling For Multiple Architectures 112 | ==================================== 113 | 114 | You can compile the package for more than one kind of computer at the 115 | same time, by placing the object files for each architecture in their 116 | own directory. To do this, you can use GNU `make'. `cd' to the 117 | directory where you want the object files and executables to go and run 118 | the `configure' script. `configure' automatically checks for the 119 | source code in the directory that `configure' is in and in `..'. This 120 | is known as a "VPATH" build. 121 | 122 | With a non-GNU `make', it is safer to compile the package for one 123 | architecture at a time in the source code directory. After you have 124 | installed the package for one architecture, use `make distclean' before 125 | reconfiguring for another architecture. 126 | 127 | On MacOS X 10.5 and later systems, you can create libraries and 128 | executables that work on multiple system types--known as "fat" or 129 | "universal" binaries--by specifying multiple `-arch' options to the 130 | compiler but only a single `-arch' option to the preprocessor. Like 131 | this: 132 | 133 | ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ 134 | CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ 135 | CPP="gcc -E" CXXCPP="g++ -E" 136 | 137 | This is not guaranteed to produce working output in all cases, you 138 | may have to build one architecture at a time and combine the results 139 | using the `lipo' tool if you have problems. 140 | 141 | Installation Names 142 | ================== 143 | 144 | By default, `make install' installs the package's commands under 145 | `/usr/local/bin', include files under `/usr/local/include', etc. You 146 | can specify an installation prefix other than `/usr/local' by giving 147 | `configure' the option `--prefix=PREFIX', where PREFIX must be an 148 | absolute file name. 149 | 150 | You can specify separate installation prefixes for 151 | architecture-specific files and architecture-independent files. If you 152 | pass the option `--exec-prefix=PREFIX' to `configure', the package uses 153 | PREFIX as the prefix for installing programs and libraries. 154 | Documentation and other data files still use the regular prefix. 155 | 156 | In addition, if you use an unusual directory layout you can give 157 | options like `--bindir=DIR' to specify different values for particular 158 | kinds of files. Run `configure --help' for a list of the directories 159 | you can set and what kinds of files go in them. In general, the 160 | default for these options is expressed in terms of `${prefix}', so that 161 | specifying just `--prefix' will affect all of the other directory 162 | specifications that were not explicitly provided. 163 | 164 | The most portable way to affect installation locations is to pass the 165 | correct locations to `configure'; however, many packages provide one or 166 | both of the following shortcuts of passing variable assignments to the 167 | `make install' command line to change installation locations without 168 | having to reconfigure or recompile. 169 | 170 | The first method involves providing an override variable for each 171 | affected directory. For example, `make install 172 | prefix=/alternate/directory' will choose an alternate location for all 173 | directory configuration variables that were expressed in terms of 174 | `${prefix}'. Any directories that were specified during `configure', 175 | but not in terms of `${prefix}', must each be overridden at install 176 | time for the entire installation to be relocated. The approach of 177 | makefile variable overrides for each directory variable is required by 178 | the GNU Coding Standards, and ideally causes no recompilation. 179 | However, some platforms have known limitations with the semantics of 180 | shared libraries that end up requiring recompilation when using this 181 | method, particularly noticeable in packages that use GNU Libtool. 182 | 183 | The second method involves providing the `DESTDIR' variable. For 184 | example, `make install DESTDIR=/alternate/directory' will prepend 185 | `/alternate/directory' before all installation names. The approach of 186 | `DESTDIR' overrides is not required by the GNU Coding Standards, and 187 | does not work on platforms that have drive letters. On the other hand, 188 | it does better at avoiding recompilation issues, and works well even 189 | when some directory options were not specified in terms of `${prefix}' 190 | at `configure' time. 191 | 192 | Optional Features 193 | ================= 194 | 195 | If the package supports it, you can cause programs to be installed 196 | with an extra prefix or suffix on their names by giving `configure' the 197 | option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. 198 | 199 | Some packages pay attention to `--enable-FEATURE' options to 200 | `configure', where FEATURE indicates an optional part of the package. 201 | They may also pay attention to `--with-PACKAGE' options, where PACKAGE 202 | is something like `gnu-as' or `x' (for the X Window System). The 203 | `README' should mention any `--enable-' and `--with-' options that the 204 | package recognizes. 205 | 206 | For packages that use the X Window System, `configure' can usually 207 | find the X include and library files automatically, but if it doesn't, 208 | you can use the `configure' options `--x-includes=DIR' and 209 | `--x-libraries=DIR' to specify their locations. 210 | 211 | Some packages offer the ability to configure how verbose the 212 | execution of `make' will be. For these packages, running `./configure 213 | --enable-silent-rules' sets the default to minimal output, which can be 214 | overridden with `make V=1'; while running `./configure 215 | --disable-silent-rules' sets the default to verbose, which can be 216 | overridden with `make V=0'. 217 | 218 | Particular systems 219 | ================== 220 | 221 | On HP-UX, the default C compiler is not ANSI C compatible. If GNU 222 | CC is not installed, it is recommended to use the following options in 223 | order to use an ANSI C compiler: 224 | 225 | ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" 226 | 227 | and if that doesn't work, install pre-built binaries of GCC for HP-UX. 228 | 229 | On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot 230 | parse its `' header file. The option `-nodtk' can be used as 231 | a workaround. If GNU CC is not installed, it is therefore recommended 232 | to try 233 | 234 | ./configure CC="cc" 235 | 236 | and if that doesn't work, try 237 | 238 | ./configure CC="cc -nodtk" 239 | 240 | On Solaris, don't put `/usr/ucb' early in your `PATH'. This 241 | directory contains several dysfunctional programs; working variants of 242 | these programs are available in `/usr/bin'. So, if you need `/usr/ucb' 243 | in your `PATH', put it _after_ `/usr/bin'. 244 | 245 | On Haiku, software installed for all users goes in `/boot/common', 246 | not `/usr/local'. It is recommended to use the following options: 247 | 248 | ./configure --prefix=/boot/common 249 | 250 | Specifying the System Type 251 | ========================== 252 | 253 | There may be some features `configure' cannot figure out 254 | automatically, but needs to determine by the type of machine the package 255 | will run on. Usually, assuming the package is built to be run on the 256 | _same_ architectures, `configure' can figure that out, but if it prints 257 | a message saying it cannot guess the machine type, give it the 258 | `--build=TYPE' option. TYPE can either be a short name for the system 259 | type, such as `sun4', or a canonical name which has the form: 260 | 261 | CPU-COMPANY-SYSTEM 262 | 263 | where SYSTEM can have one of these forms: 264 | 265 | OS 266 | KERNEL-OS 267 | 268 | See the file `config.sub' for the possible values of each field. If 269 | `config.sub' isn't included in this package, then this package doesn't 270 | need to know the machine type. 271 | 272 | If you are _building_ compiler tools for cross-compiling, you should 273 | use the option `--target=TYPE' to select the type of system they will 274 | produce code for. 275 | 276 | If you want to _use_ a cross compiler, that generates code for a 277 | platform different from the build platform, you should specify the 278 | "host" platform (i.e., that on which the generated programs will 279 | eventually be run) with `--host=TYPE'. 280 | 281 | Sharing Defaults 282 | ================ 283 | 284 | If you want to set default values for `configure' scripts to share, 285 | you can create a site shell script called `config.site' that gives 286 | default values for variables like `CC', `cache_file', and `prefix'. 287 | `configure' looks for `PREFIX/share/config.site' if it exists, then 288 | `PREFIX/etc/config.site' if it exists. Or, you can set the 289 | `CONFIG_SITE' environment variable to the location of the site script. 290 | A warning: not all `configure' scripts look for a site script. 291 | 292 | Defining Variables 293 | ================== 294 | 295 | Variables not defined in a site shell script can be set in the 296 | environment passed to `configure'. However, some packages may run 297 | configure again during the build, and the customized values of these 298 | variables may be lost. In order to avoid this problem, you should set 299 | them in the `configure' command line, using `VAR=value'. For example: 300 | 301 | ./configure CC=/usr/local2/bin/gcc 302 | 303 | causes the specified `gcc' to be used as the C compiler (unless it is 304 | overridden in the site shell script). 305 | 306 | Unfortunately, this technique does not work for `CONFIG_SHELL' due to 307 | an Autoconf bug. Until the bug is fixed you can use this workaround: 308 | 309 | CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash 310 | 311 | `configure' Invocation 312 | ====================== 313 | 314 | `configure' recognizes the following options to control how it 315 | operates. 316 | 317 | `--help' 318 | `-h' 319 | Print a summary of all of the options to `configure', and exit. 320 | 321 | `--help=short' 322 | `--help=recursive' 323 | Print a summary of the options unique to this package's 324 | `configure', and exit. The `short' variant lists options used 325 | only in the top level, while the `recursive' variant lists options 326 | also present in any nested packages. 327 | 328 | `--version' 329 | `-V' 330 | Print the version of Autoconf used to generate the `configure' 331 | script, and exit. 332 | 333 | `--cache-file=FILE' 334 | Enable the cache: use and save the results of the tests in FILE, 335 | traditionally `config.cache'. FILE defaults to `/dev/null' to 336 | disable caching. 337 | 338 | `--config-cache' 339 | `-C' 340 | Alias for `--cache-file=config.cache'. 341 | 342 | `--quiet' 343 | `--silent' 344 | `-q' 345 | Do not print messages saying which checks are being made. To 346 | suppress all normal output, redirect it to `/dev/null' (any error 347 | messages will still be shown). 348 | 349 | `--srcdir=DIR' 350 | Look for the package's source code in directory DIR. Usually 351 | `configure' can determine that directory automatically. 352 | 353 | `--prefix=DIR' 354 | Use DIR as the installation prefix. *note Installation Names:: 355 | for more details, including other options available for fine-tuning 356 | the installation locations. 357 | 358 | `--no-create' 359 | `-n' 360 | Run the configure checks, but stop before creating any output 361 | files. 362 | 363 | `configure' also accepts some other, not widely useful, options. Run 364 | `configure --help' for more details. 365 | 366 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = src 2 | dist_sysconf_DATA = rtp2httpd.conf 3 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | Version 0.4 2 | - Fixed zombie processes creation when more than one 3 | client disconnects in a short period of time. 4 | 5 | Version 0.3 6 | - Support for RAW UDP (MUDP) streams 7 | 8 | Version 0.2 9 | - Fix address in use when two clients wanted same 10 | source mRTP stream. 11 | - Changed configfile and commandline options 12 | resolving order so commandline opts do override 13 | configfile defaults. 14 | 15 | Version 0.1 16 | - Initial release 17 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | # IPTV 组播转单播,支持 FCC 快速换台 2 | 3 | 本 fork 在原版 run2httpd 基础上增加了 [FCC 协议支持](https://blog.csdn.net/yangzex/article/details/131328837?fcc=10.255.14.152:15970),可以实现像原生 IPTV 盒子一样的快速换台。 4 | 5 | ## 快速换台效果 6 | 7 | https://github.com/stackia/rtp2httpd/assets/5107241/fe390221-ad0f-4f10-8cec-fb32793aaca0 8 | 9 | https://github.com/stackia/rtp2httpd/assets/5107241/54da27fc-ca3a-438b-bd67-11ed2405a018 10 | 11 | 注:rtp2httpd 搭配 FCC server 后,仅保证可以快速提供可立即解码的视频流给客户端。具体换台速度能有多快,还取决于视频客户端的实现,客户端的 buffer 越小,换台速度越快。 12 | 13 | ## 使用方法 14 | 15 | ### OpenWrt 16 | 17 | 可以在 [Releases](https://github.com/stackia/rtp2httpd/releases) 页面下载编译好的 ipk 包直接使用。 18 | 19 | 下载并安装 `rtp2httpd_x.y.z-1_.ipk` / `luci-app-rtp2httpd_x.y.z_all.ipk` / `luci-i18n-rtp2httpd-en_x.y.z_all.ipk` / `luci-i18n-rtp2httpd-zh-cn_x.y.z_all.ipk` 四个包。然后在 LuCI 中配置 rtp2httpd 的启动参数即可。 20 | 21 | Image 22 | 23 | ### Docker 24 | 25 | 仅支持运行在 host 网络模式下。 26 | 27 | ```sh 28 | docker run --network=host --rm ghcr.io/stackia/rtp2httpd:latest --noconfig --verbose 3 --listen 5140 --maxclients 50 29 | ``` 30 | 31 | 启动 rtp2httpd 后,可以使用类似 `http://10.0.0.2:5140/rtp/239.253.64.120:5140?fcc=10.255.14.152:15970` 这样的地址将组播源转换为单播源。这里的 FCC 服务器地址需要在自己所在地的 IPTV 盒子上抓包获得(关键词 `ChannelFCCIP` / `ChannelFCCPort`,通常和直播源 `ChannelURL` 一起出现),并在路由器上添加静态路由走 IPTV 网络出站。 32 | 33 | 并非所有地区都有 FCC 服务器,如果抓不到,那么很可能当地运营商没有部署 IPTV FCC 服务器。去掉链接里的 `?fcc=` 可以只使用组播转单播功能。 34 | 35 | 如果不是直接运行在路由器上,需要开启 `fcc-nat-traversal` 选项。如果路由器支持 NAT-PMP 功能(通常在 UPnP 相关设置下面),建议设置为 `fcc-nat-traversal=2`,播放最稳定。如果不支持 NAT-PMP,则设置为 `fcc-nat-traversal=1`,此时某些情况下播放会不稳定,如果发现不能播放,需要重试几次。 36 | 37 | 如果直接运行在路由器上,则无需开启 `fcc-nat-traversal`(或者设置为 `0`)。 38 | 39 | ### 参数说明 40 | 41 | ```text 42 | Usage: rtp2httpd [options] 43 | 44 | Options: 45 | -h --help Show this help 46 | -v --verbose Increase verbosity 47 | -q --quiet Report only fatal errors 48 | -d --daemon Fork to background (implies -q) 49 | -D --nodaemon Do not daemonise. (default) 50 | -U --noudpxy Disable UDPxy compatibility 51 | -m --maxclients Serve max n requests simultaneously (dfl 5) 52 | -l --listen [addr:]port Address/port to bind (default ANY:8080) 53 | -c --config Read this file, instead of 54 | -C --noconfig Do not read the default config 55 | -n --fcc-nat-traversal <0/1/2> NAT traversal for FCC media stream, 0=disabled, 1=punchhole, 2=NAT-PMP (default 0) 56 | -H --hostname Hostname to check in the Host: HTTP header (default none) 57 | -i --upstream-interface Interface to use for requesting upstream media stream (default none, which follows the routing table) 58 | ``` 59 | 60 | ### rtp2httpd README 61 | 62 | ```rst 63 | Multicast RTP to Unicast HTTP stream convertor with FCC support! 64 | ============================================== 65 | 66 | Copyright (c) 2008-2020 Ondřej Caletka 67 | 68 | What is it 69 | ---------- 70 | 71 | This program converts multicast RTP/UDP media into http stream. 72 | It acts as a tiny HTTP server. When client connect, 73 | pre-configured multicast RTP service is choosen by URL. 74 | Program then join pre-configured multicast address and translate 75 | incoming RTP data to HTTP stream. 76 | 77 | As an alternative to pre-configured address, there is also a [UDPxy][1] 78 | compatibility mode. If URL looks like `//:` 79 | and UDPxy mode is enabled (which is default), the program joins address 80 | contained in the URL. 81 | 82 | It's main purpose is to remotely watch multicast video and audio 83 | broadcast, when your internet connection in the first-mile 84 | (e.g. broadband router) does not support multicast routing. 85 | 86 | Main advantage over translating streams in vlc (http://www.videolan.org) 87 | is that multicast group is joined _after_ the HTTP client connects, 88 | and is leaved immediately after HTTP client disconnects. So, 89 | server can be run all the time and consume almost no bandwidth nor CPU 90 | power, until HTTP client connects. 91 | 92 | [1]: http://www.udpxy.com/index-en.html 93 | 94 | Installation 95 | ------------ 96 | 97 | The package uses GNU autotools. See the file `INSTALL` for details. 98 | 99 | Configuration 100 | ------------- 101 | 102 | See provided configfile for example, run program with `--help` for 103 | a list of command line switches. 104 | 105 | __Do not run rtp2httpd as root. Choose some unprivileged port number and run 106 | it under unprivileged user account.__ 107 | ``` 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | README -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_PREREQ([2.69]) 5 | AC_INIT([rtp2httpd-fcc], [1.0.0], [jsq2627@gmail.com]) 6 | AC_CONFIG_SRCDIR([src/rtp2httpd.h]) 7 | AC_CONFIG_HEADERS([src/config.h]) 8 | AM_INIT_AUTOMAKE 9 | 10 | # Checks for programs. 11 | AC_PROG_CC 12 | 13 | # Checks for libraries. 14 | 15 | # Checks for header files. 16 | AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdlib.h string.h strings.h sys/socket.h unistd.h]) 17 | 18 | # Checks for typedefs, structures, and compiler characteristics. 19 | AC_C_INLINE 20 | AC_TYPE_PID_T 21 | AC_TYPE_SIZE_T 22 | AC_TYPE_UINT16_T 23 | AC_TYPE_UINT8_T 24 | 25 | # Checks for library functions. 26 | AC_FUNC_FORK 27 | AC_CHECK_FUNCS([memmove memset select socket strcasecmp strdup strerror strndup getopt_long getaddrinfo getnameinfo]) 28 | 29 | AC_CONFIG_FILES([Makefile 30 | src/Makefile]) 31 | AC_OUTPUT 32 | -------------------------------------------------------------------------------- /openwrt-support/luci-app-rtp2httpd/Makefile: -------------------------------------------------------------------------------- 1 | include $(TOPDIR)/rules.mk 2 | 3 | PKG_NAME:=luci-app-rtp2httpd 4 | PKG_VERSION:=1.0.0 5 | PKG_PO_VERSION:=1.0.0 6 | PKG_RELEASE:=1 7 | 8 | LUCI_TITLE:=LuCI Support for rtp2httpd 9 | LUCI_PKGARCH:=all 10 | LUCI_DEPENDS:=+rtp2httpd 11 | 12 | PKG_MAINTAINER:=Stackie Jia 13 | 14 | include $(TOPDIR)/feeds/luci/luci.mk 15 | 16 | # call BuildPackage - OpenWrt buildroot signature 17 | -------------------------------------------------------------------------------- /openwrt-support/luci-app-rtp2httpd/htdocs/luci-static/resources/view/rtp2httpd.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | "require form"; 3 | "require view"; 4 | "require tools.widgets as widgets"; 5 | 6 | return view.extend({ 7 | render: function () { 8 | var m, s, o; 9 | 10 | m = new form.Map( 11 | "rtp2httpd", 12 | _("rtp2httpd"), 13 | _( 14 | "rtp2httpd converts multicast RTP/UDP media into http stream. Here you can configure the settings." 15 | ) 16 | ); 17 | 18 | s = m.section(form.TypedSection, "rtp2httpd"); 19 | s.anonymous = true; 20 | s.addremove = true; 21 | 22 | o = s.option(form.Flag, "disabled", _("rtp2httpd_Enabled")); 23 | o.enabled = "0"; 24 | o.disabled = "1"; 25 | o.default = o.enabled; 26 | o.rmempty = false; 27 | 28 | o = s.option( 29 | form.Flag, 30 | "respawn", 31 | _("rtp2httpd_Respawn"), 32 | _("rtp2httpd_Auto restart after crash") 33 | ); 34 | o.default = "0"; 35 | 36 | o = s.option(form.Value, "port", _("rtp2httpd_Port")); 37 | o.datatype = "port"; 38 | o.default = "8080"; 39 | 40 | o = s.option(form.ListValue, "verbose", _("rtp2httpd_Verbose")); 41 | o.value("0", _("rtp2httpd_Quiet")); 42 | o.value("1", _("rtp2httpd_Error")); 43 | o.value("2", _("rtp2httpd_Info")); 44 | o.value("3", _("rtp2httpd_Debug")); 45 | o.default = "1"; 46 | 47 | o = s.option( 48 | widgets.DeviceSelect, 49 | "upstream_interface", 50 | _("rtp2httpd_Upstream Interface"), 51 | _( 52 | "rtp2httpd_Interface to use for requesting upstream media stream (default none, which follows the routing table)" 53 | ) 54 | ); 55 | o.noaliases = true; 56 | o.datatype = "interface"; 57 | 58 | o = s.option(form.Value, "maxclients", _("rtp2httpd_Max clients")); 59 | o.datatype = "range(1, 5000)"; 60 | o.default = "5"; 61 | 62 | o = s.option( 63 | form.Value, 64 | "hostname", 65 | _("rtp2httpd_Hostname"), 66 | _("rtp2httpd_Hostname to check in the Host: HTTP header") 67 | ); 68 | o.datatype = "hostname"; 69 | 70 | o = s.option( 71 | form.ListValue, 72 | "fcc_nat_traversal", 73 | _("rtp2httpd_FCC NAT traversal"), 74 | _("rtp2httpd_Only needed when used as a downstream router") 75 | ); 76 | o.value("0", _("rtp2httpd_Don't use NAT traversal")); 77 | o.value("1", _("rtp2httpd_NAT punch hole")); 78 | o.value("2", _("rtp2httpd_NAT-PMP")); 79 | o.default = "0"; 80 | 81 | return m.render(); 82 | }, 83 | }); 84 | -------------------------------------------------------------------------------- /openwrt-support/luci-app-rtp2httpd/po/en/rtp2httpd.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Content-Type: text/plain; charset=UTF-8\n" 4 | "Project-Id-Version: PACKAGE VERSION\n" 5 | "Last-Translator: Automatically generated\n" 6 | "Language-Team: none\n" 7 | "Language: en\n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Transfer-Encoding: 8bit\n" 10 | 11 | msgid "rtp2httpd" 12 | msgstr "rtp2httpd" 13 | 14 | msgid "rtp2httpd converts multicast RTP/UDP media into http stream. Here you can configure the settings." 15 | msgstr "rtp2httpd converts multicast RTP/UDP media into http stream. Here you can configure the settings." 16 | 17 | msgid "rtp2httpd_Enabled" 18 | msgstr "Enabled" 19 | 20 | msgid "rtp2httpd_Respawn" 21 | msgstr "Respawn" 22 | 23 | msgid "rtp2httpd_Auto restart after crash" 24 | msgstr "Auto restart after crash" 25 | 26 | msgid "rtp2httpd_Port" 27 | msgstr "Port" 28 | 29 | msgid "rtp2httpd_Verbose" 30 | msgstr "Logging level" 31 | 32 | msgid "rtp2httpd_Quiet" 33 | msgstr "Quiet" 34 | 35 | msgid "rtp2httpd_Error" 36 | msgstr "Error" 37 | 38 | msgid "rtp2httpd_Info" 39 | msgstr "Info" 40 | 41 | msgid "rtp2httpd_Debug" 42 | msgstr "Debug" 43 | 44 | msgid "rtp2httpd_Upstream Interface" 45 | msgstr "Upstream Interface" 46 | 47 | msgid "rtp2httpd_Interface to use for requesting upstream media stream (default none, which follows the routing table)" 48 | msgstr "Interface to use for requesting upstream media stream (default none, which follows the routing table)" 49 | 50 | msgid "rtp2httpd_Max clients" 51 | msgstr "Max clients allowed" 52 | 53 | msgid "rtp2httpd_Hostname" 54 | msgstr "Hostname" 55 | 56 | msgid "rtp2httpd_Hostname to check in the Host: HTTP header" 57 | msgstr "If enabled, rtp2httpd will only allow connections if the 'Host:' HTTP header matches the hostname" 58 | 59 | msgid "rtp2httpd_FCC NAT traversal" 60 | msgstr "FCC NAT traversal mode" 61 | 62 | msgid "rtp2httpd_Only needed when used as a downstream router" 63 | msgstr "Only needed when used as a downstream router" 64 | 65 | msgid "rtp2httpd_Don't use NAT traversal" 66 | msgstr "Don't use NAT traversal" 67 | 68 | msgid "rtp2httpd_NAT punch hole" 69 | msgstr "NAT punch hole (Low reliability)" 70 | 71 | msgid "rtp2httpd_NAT-PMP" 72 | msgstr "NAT-PMP (Requires upstream router support)" 73 | 74 | -------------------------------------------------------------------------------- /openwrt-support/luci-app-rtp2httpd/po/zh_Hans/rtp2httpd.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Content-Type: text/plain; charset=UTF-8\n" 4 | "Project-Id-Version: PACKAGE VERSION\n" 5 | "Last-Translator: Automatically generated\n" 6 | "Language-Team: none\n" 7 | "Language: zh_Hans\n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Transfer-Encoding: 8bit\n" 10 | 11 | msgid "rtp2httpd" 12 | msgstr "rtp2httpd" 13 | 14 | msgid "rtp2httpd converts multicast RTP/UDP media into http stream. Here you can configure the settings." 15 | msgstr "rtp2httpd 将组播 RTP/UDP 媒体转换为 HTTP 流。在这里进行配置。" 16 | 17 | msgid "rtp2httpd_Enabled" 18 | msgstr "启用" 19 | 20 | msgid "rtp2httpd_Respawn" 21 | msgstr "自动重启" 22 | 23 | msgid "rtp2httpd_Auto restart after crash" 24 | msgstr "程序崩溃后自动重启" 25 | 26 | msgid "rtp2httpd_Port" 27 | msgstr "端口" 28 | 29 | msgid "rtp2httpd_Verbose" 30 | msgstr "日志级别" 31 | 32 | msgid "rtp2httpd_Quiet" 33 | msgstr "Quiet" 34 | 35 | msgid "rtp2httpd_Error" 36 | msgstr "Error" 37 | 38 | msgid "rtp2httpd_Info" 39 | msgstr "Info" 40 | 41 | msgid "rtp2httpd_Debug" 42 | msgstr "Debug" 43 | 44 | msgid "rtp2httpd_Upstream Interface" 45 | msgstr "上游接口" 46 | 47 | msgid "rtp2httpd_Interface to use for requesting upstream media stream (default none, which follows the routing table)" 48 | msgstr "用于请求上游媒体流(组播流或 FCC 单播流)的接口(默认不指定,跟随路由表)" 49 | 50 | msgid "rtp2httpd_Max clients" 51 | msgstr "最大客户端数" 52 | 53 | msgid "rtp2httpd_Hostname" 54 | msgstr "仅允许使用域名连接" 55 | 56 | msgid "rtp2httpd_Hostname to check in the Host: HTTP header" 57 | msgstr "开启后,只有 HTTP Host 头匹配指定域名时,rtp2httpd 才会允许连接" 58 | 59 | msgid "rtp2httpd_FCC NAT traversal" 60 | msgstr "FCC NAT 穿透模式" 61 | 62 | msgid "rtp2httpd_Only needed when used as a downstream router" 63 | msgstr "仅当作为二级路由并需要使用 FCC(快速频道切换)时启用" 64 | 65 | msgid "rtp2httpd_Don't use NAT traversal" 66 | msgstr "不使用 NAT 穿透" 67 | 68 | msgid "rtp2httpd_NAT punch hole" 69 | msgstr "NAT 打洞(可靠性较低)" 70 | 71 | msgid "rtp2httpd_NAT-PMP" 72 | msgstr "NAT-PMP(需要上级路由器支持)" 73 | -------------------------------------------------------------------------------- /openwrt-support/luci-app-rtp2httpd/root/usr/share/luci/menu.d/luci-app-rtp2httpd.json: -------------------------------------------------------------------------------- 1 | { 2 | "admin/services/rtp2httpd": { 3 | "title": "rtp2httpd", 4 | "action": { 5 | "type": "view", 6 | "path": "rtp2httpd" 7 | }, 8 | "depends": { 9 | "acl": ["luci-app-rtp2httpd"], 10 | "uci": { "rtp2httpd": true } 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /openwrt-support/luci-app-rtp2httpd/root/usr/share/rpcd/acl.d/luci-app-rtp2httpd.json: -------------------------------------------------------------------------------- 1 | { 2 | "luci-app-rtp2httpd": { 3 | "description": "Grant UCI access for luci-app-rtp2httpd", 4 | "read": { 5 | "uci": ["rtp2httpd"] 6 | }, 7 | "write": { 8 | "uci": ["rtp2httpd"] 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /openwrt-support/rtp2httpd/Makefile: -------------------------------------------------------------------------------- 1 | include $(TOPDIR)/rules.mk 2 | 3 | PKG_NAME:=rtp2httpd 4 | PKG_VERSION:=1.0.0 5 | PKG_RELEASE:=1 6 | PKG_MAINTAINER:=Stackie Jia 7 | 8 | PKG_BUILD_DIR:=$(BUILD_DIR)/rtp2httpd-$(PKG_VERSION) 9 | PKG_FIXUP:=autoreconf 10 | 11 | include $(INCLUDE_DIR)/package.mk 12 | 13 | define Package/rtp2httpd 14 | SECTION:=net 15 | CATEGORY:=Network 16 | TITLE:=Convert multicast RTP/UDP IPTV streams into HTTP streams with FCC support 17 | URL:=https://github.com/stackia/rtp2httpd 18 | endef 19 | 20 | define Package/rtp2httpd/description 21 | rtp2httpd converts multicast RTP/UDP media into http stream. 22 | It acts as a tiny HTTP server. When client connect, 23 | pre-configured multicast RTP service is choosen by URL. 24 | Program then join pre-configured multicast address and translate 25 | incoming RTP data to HTTP stream. 26 | endef 27 | 28 | define Package/rtp2httpd/conffiles 29 | /etc/config/rtp2httpd 30 | endef 31 | 32 | define Package/rtp2httpd/install 33 | $(INSTALL_DIR) $(1)/etc/init.d $(1)/etc/config 34 | $(INSTALL_CONF) ./files/rtp2httpd.conf $(1)/etc/config/rtp2httpd 35 | $(INSTALL_BIN) ./files/rtp2httpd.init $(1)/etc/init.d/rtp2httpd 36 | $(INSTALL_DIR) $(1)/usr/bin/ 37 | $(INSTALL_BIN) $(PKG_BUILD_DIR)/src/rtp2httpd $(1)/usr/bin 38 | endef 39 | 40 | $(eval $(call BuildPackage,rtp2httpd)) 41 | -------------------------------------------------------------------------------- /openwrt-support/rtp2httpd/files/rtp2httpd.conf: -------------------------------------------------------------------------------- 1 | config rtp2httpd 2 | option disabled '1' 3 | option respawn '1' 4 | option port '5140' 5 | # option verbose '2' 6 | # option upstream_interface 'iptv' 7 | # option maxclients '5' 8 | # option hostname 'somehost.example.com' 9 | # option fcc_nat_traversal '0' 10 | -------------------------------------------------------------------------------- /openwrt-support/rtp2httpd/files/rtp2httpd.init: -------------------------------------------------------------------------------- 1 | #!/bin/sh /etc/rc.common 2 | 3 | START=50 4 | USE_PROCD=1 5 | 6 | append_arg() { 7 | local cfg="$1" 8 | local var="$2" 9 | local opt="$3" 10 | local def="$4" 11 | local val 12 | 13 | config_get val "$cfg" "$var" 14 | [ -n "$val" -o -n "$def" ] && procd_append_param command $opt "${val:-$def}" 15 | } 16 | 17 | start_instance() { 18 | local cfg="$1" 19 | local aux 20 | 21 | config_get_bool aux "$cfg" 'disabled' '0' 22 | [ "$aux" = 1 ] && return 1 23 | 24 | procd_open_instance 25 | 26 | procd_set_param command /usr/bin/rtp2httpd 27 | procd_append_param command "--noconfig" 28 | 29 | append_arg "$cfg" verbose "--verbose" 30 | append_arg "$cfg" port "--listen" 31 | append_arg "$cfg" maxclients "--maxclients" 32 | append_arg "$cfg" hostname "--hostname" 33 | append_arg "$cfg" fcc_nat_traversal "--fcc-nat-traversal" 34 | append_arg "$cfg" upstream_interface "--upstream-interface" 35 | 36 | config_get_bool aux "$cfg" 'respawn' '0' 37 | [ "$aux" = 1 ] && procd_set_param respawn 38 | 39 | procd_set_param stdout 1 40 | procd_set_param stderr 1 41 | 42 | procd_close_instance 43 | } 44 | 45 | service_triggers() { 46 | procd_add_reload_trigger "rtp2httpd" 47 | } 48 | 49 | start_service() { 50 | config_load rtp2httpd 51 | config_foreach start_instance rtp2httpd 52 | } 53 | -------------------------------------------------------------------------------- /rtp2httpd.conf: -------------------------------------------------------------------------------- 1 | #RTP2HTTPD Config file 2 | 3 | # All blank lines and lines starting with # or ; are ignored 4 | [global] 5 | #GLOBAL OPTIONS 6 | #These options can be overrided by command-line switches 7 | 8 | #VERBOSITY: 0-quiet 1-error 2-info 3-debug (default 1) 9 | verbosity = 2 10 | 11 | #maximum paralell clients (default 5) 12 | ;maxclients = 5 13 | 14 | #wheather daemonise (default no) 15 | ;daemonise = no 16 | 17 | # UDPxy URL compatibility (default yes) 18 | ;udpxy = yes 19 | 20 | # Hostname to check in the Host: HTTP header (default none) 21 | ;hostname = somehost.example.com 22 | 23 | # NAT traversal for FCC media stream 24 | # 0: disabled, 1: NAT punchhole (maybe unreliable), 2: NAT-PMP 25 | ;fcc-nat-traversal = 0 26 | 27 | # Interface to use for requesting upstream media stream (default none, which follows the routing table) 28 | ;upstream-interface = iptv 29 | 30 | [bind] 31 | #List of address and ports to bind to, eg. 32 | ;mybox.example.net 8080 33 | ;mybox2.example.net 8000 34 | ;2001::1 http-alt 35 | 36 | #Note that binding to port number < 1024 will 37 | #require root privelegies and therefore is 38 | #not recommended. (Dropping privilegies is 39 | #not implemeted at this moment) 40 | 41 | #below is default - all addresses, port 8080 42 | * 8080 43 | 44 | [services] 45 | #Format: 46 | #SERVICE_URL TYPE=MRTP MADDR MPORT 47 | # 48 | #TYPE may be MRTP for RTP/UDP streams 49 | #or MUDP for RAW UDP streams 50 | # 51 | # MADDR can contain @ 52 | 53 | ;ct1 MRTP 239.194.10.11 1234 54 | ;ct2 MRTP 239.194.10.12 1234 55 | ;nova MRTP 192.0.2.1@239.194.10.13 1234 56 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | bin_PROGRAMS = rtp2httpd 3 | 4 | rtp2httpd_SOURCES = rtp2httpd.c httpclients.c configuration.c 5 | 6 | noinst_HEADERS = rtp2httpd.h 7 | 8 | AM_CFLAGS= -DSYSCONFDIR=\"@sysconfdir@\" 9 | -------------------------------------------------------------------------------- /src/configuration.c: -------------------------------------------------------------------------------- 1 | /* 2 | * RTP2HTTP Proxy - Multicast RTP stream to UNICAST HTTP translator 3 | * 4 | * Copyright (C) 2008-2010 Ondrej Caletka 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License version 2 8 | * as published by the Free Software Foundation. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program (see the file COPYING included with this 17 | * distribution); if not, write to the Free Software Foundation, Inc., 18 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #define _GNU_SOURCE 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "rtp2httpd.h" 38 | 39 | #ifdef HAVE_CONFIG_H 40 | #include "config.h" 41 | #endif /* HAVE_CONFIG_H */ 42 | 43 | #define MAX_LINE 150 44 | 45 | /* GLOBAL CONFIGURATION VARIABLES */ 46 | 47 | enum loglevel conf_verbosity; 48 | int conf_daemonise; 49 | int conf_udpxy; 50 | int conf_maxclients; 51 | char *conf_hostname = NULL; 52 | struct ifreq conf_upstream_interface; 53 | enum fcc_nat_traversal conf_fcc_nat_traversal; 54 | 55 | /* *** */ 56 | 57 | int cmd_verbosity_set; 58 | int cmd_daemonise_set; 59 | int cmd_udpxy_set; 60 | int cmd_maxclients_set; 61 | int cmd_bind_set; 62 | int cmd_fcc_nat_traversal_set; 63 | int cmd_hostname_set; 64 | int cmd_upstream_interface_set; 65 | 66 | enum section_e 67 | { 68 | SEC_NONE = 0, 69 | SEC_BIND, 70 | SEC_SERVICES, 71 | SEC_GLOBAL 72 | }; 73 | 74 | void parseBindSec(char *line) 75 | { 76 | int i, j; 77 | char *node, *service; 78 | struct bindaddr_s *ba; 79 | 80 | j = i = 0; 81 | while (!isspace(line[j])) 82 | j++; 83 | node = strndup(line, j); 84 | 85 | i = j; 86 | while (isspace(line[i])) 87 | i++; 88 | j = i; 89 | while (!isspace(line[j])) 90 | j++; 91 | service = strndup(line + i, j - i); 92 | 93 | if (strcmp("*", node) == 0) 94 | { 95 | free(node); 96 | node = NULL; 97 | } 98 | logger(LOG_DEBUG, "node: %s, port: %s\n", node, service); 99 | 100 | ba = malloc(sizeof(struct bindaddr_s)); 101 | ba->node = node; 102 | ba->service = service; 103 | ba->next = bindaddr; 104 | bindaddr = ba; 105 | } 106 | 107 | void parseServicesSec(char *line) 108 | { 109 | int i, j, r, rr; 110 | struct addrinfo hints; 111 | char *servname, *type, *maddr, *mport, *msrc = "", *msaddr = "", *msport = ""; 112 | struct services_s *service; 113 | 114 | memset(&hints, 0, sizeof(hints)); 115 | hints.ai_socktype = SOCK_DGRAM; 116 | 117 | j = i = 0; 118 | while (!isspace(line[j])) 119 | j++; 120 | servname = strndup(line, j); 121 | 122 | i = j; 123 | while (isspace(line[i])) 124 | i++; 125 | j = i; 126 | while (!isspace(line[j])) 127 | j++; 128 | type = strndupa(line + i, j - i); 129 | 130 | i = j; 131 | while (isspace(line[i])) 132 | i++; 133 | j = i; 134 | while (!isspace(line[j])) 135 | j++; 136 | maddr = strndupa(line + i, j - i); 137 | 138 | i = j; 139 | while (isspace(line[i])) 140 | i++; 141 | j = i; 142 | while (!isspace(line[j])) 143 | j++; 144 | mport = strndupa(line + i, j - i); 145 | 146 | if (strstr(maddr, "@") != NULL) 147 | { 148 | char *split; 149 | char *current; 150 | int cnt = 0; 151 | split = strtok(maddr, "@"); 152 | while (split != NULL) 153 | { 154 | current = split; 155 | if (cnt == 0) 156 | msrc = current; 157 | split = strtok(NULL, "@"); 158 | if (cnt > 0 && split != NULL) 159 | { 160 | strcat(msrc, "@"); 161 | strcat(msrc, current); 162 | } 163 | if (cnt > 0 && split == NULL) 164 | maddr = current; 165 | cnt++; 166 | } 167 | 168 | cnt = 0; 169 | msaddr = msrc; 170 | split = strtok(msrc, ":"); 171 | while (split != NULL) 172 | { 173 | current = split; 174 | if (cnt == 0) 175 | msaddr = current; 176 | split = strtok(NULL, ":"); 177 | if (cnt > 0 && split != NULL) 178 | { 179 | strcat(msaddr, ":"); 180 | strcat(msaddr, current); 181 | } 182 | if (cnt > 0 && split == NULL) 183 | msport = current; 184 | cnt++; 185 | } 186 | } 187 | 188 | logger(LOG_DEBUG, "serv: %s, type: %s, maddr: %s, mport: %s, msaddr: %s, msport: %s\n", 189 | servname, type, maddr, mport, msaddr, msport); 190 | 191 | if ((strcasecmp("MRTP", type) != 0) && (strcasecmp("MUDP", type) != 0)) 192 | { 193 | logger(LOG_ERROR, "Unsupported service type: %s\n", type); 194 | free(servname); 195 | free(msrc); 196 | return; 197 | } 198 | 199 | service = malloc(sizeof(struct services_s)); 200 | memset(service, 0, sizeof(*service)); 201 | 202 | r = getaddrinfo(maddr, mport, &hints, &(service->addr)); 203 | rr = 0; 204 | if (strcmp(msrc, "") != 0 && msrc != NULL) 205 | { 206 | rr = getaddrinfo(msaddr, msport, &hints, &(service->msrc_addr)); 207 | } 208 | if (r || rr) 209 | { 210 | if (r) 211 | { 212 | logger(LOG_ERROR, "Cannot init service %s. GAI: %s\n", 213 | servname, gai_strerror(r)); 214 | } 215 | if (rr) 216 | { 217 | logger(LOG_ERROR, "Cannot init service %s. GAI: %s\n", 218 | servname, gai_strerror(rr)); 219 | } 220 | free(servname); 221 | free(msrc); 222 | free(service); 223 | return; 224 | } 225 | if (service->addr->ai_next != NULL) 226 | { 227 | logger(LOG_ERROR, "Warning: maddr is ambiguos.\n"); 228 | } 229 | if (strcmp(msrc, "") != 0 && msrc != NULL) 230 | { 231 | if (service->msrc_addr->ai_next != NULL) 232 | { 233 | logger(LOG_ERROR, "Warning: msrc is ambiguos.\n"); 234 | } 235 | } 236 | 237 | if (strcasecmp("MRTP", type) == 0) 238 | { 239 | service->service_type = SERVICE_MRTP; 240 | } 241 | else if (strcasecmp("MUDP", type) == 0) 242 | { 243 | service->service_type = SERVICE_MUDP; 244 | } 245 | 246 | service->url = servname; 247 | service->msrc = strdup(msrc); 248 | service->next = services; 249 | services = service; 250 | } 251 | 252 | void parseGlobalSec(char *line) 253 | { 254 | int i, j; 255 | char *param, *value; 256 | char *ind; 257 | 258 | j = i = 0; 259 | while (!isspace(line[j])) 260 | j++; 261 | param = strndupa(line, j); 262 | 263 | ind = index(line + j, '='); 264 | if (ind == NULL) 265 | { 266 | logger(LOG_ERROR, "Unrecognised config line: %s\n", line); 267 | return; 268 | } 269 | 270 | i = ind - line + 1; 271 | while (isspace(line[i])) 272 | i++; 273 | j = i; 274 | while (!isspace(line[j])) 275 | j++; 276 | value = strndupa(line + i, j - i); 277 | 278 | if (strcasecmp("verbosity", param) == 0) 279 | { 280 | if (!cmd_verbosity_set) 281 | { 282 | conf_verbosity = atoi(value); 283 | } 284 | else 285 | { 286 | logger(LOG_INFO, "Warning: Config file value \"verbosity\" ignored. It's already set on CmdLine.\n"); 287 | } 288 | return; 289 | } 290 | if (strcasecmp("daemonise", param) == 0) 291 | { 292 | if (!cmd_daemonise_set) 293 | { 294 | if ((strcasecmp("on", value) == 0) || 295 | (strcasecmp("true", value) == 0) || 296 | (strcasecmp("yes", value) == 0) || 297 | (strcasecmp("1", value) == 0)) 298 | { 299 | conf_daemonise = 1; 300 | } 301 | else 302 | { 303 | conf_daemonise = 0; 304 | } 305 | } 306 | else 307 | { 308 | logger(LOG_INFO, "Warning: Config file value \"daemonise\" ignored. It's already set on CmdLine.\n"); 309 | } 310 | return; 311 | } 312 | if (strcasecmp("maxclients", param) == 0) 313 | { 314 | if (!cmd_maxclients_set) 315 | { 316 | if (atoi(value) < 1) 317 | { 318 | logger(LOG_ERROR, "Invalid maxclients! Ignoring.\n"); 319 | return; 320 | } 321 | conf_maxclients = atoi(value); 322 | } 323 | else 324 | { 325 | logger(LOG_INFO, "Warning: Config file value \"maxclients\" ignored. It's already set on CmdLine.\n"); 326 | } 327 | return; 328 | } 329 | if (strcasecmp("udpxy", param) == 0) 330 | { 331 | if (!cmd_udpxy_set) 332 | { 333 | if ((strcasecmp("on", value) == 0) || 334 | (strcasecmp("true", value) == 0) || 335 | (strcasecmp("yes", value) == 0) || 336 | (strcasecmp("1", value) == 0)) 337 | { 338 | conf_udpxy = 1; 339 | } 340 | else 341 | { 342 | conf_udpxy = 0; 343 | } 344 | } 345 | else 346 | { 347 | logger(LOG_INFO, "Warning: Config file value \"udpxy\" ignored. It's already set on CmdLine.\n"); 348 | } 349 | return; 350 | } 351 | if (strcasecmp("hostname", param) == 0) 352 | { 353 | if (!cmd_hostname_set) 354 | { 355 | conf_hostname = strdup(value); 356 | } 357 | else 358 | { 359 | logger(LOG_INFO, "Warning: Config file value \"hostname\" ignored. It's already set on CmdLine.\n"); 360 | } 361 | return; 362 | } 363 | if (strcasecmp("upstream-interface", param) == 0) 364 | { 365 | if (!cmd_upstream_interface_set) 366 | { 367 | strncpy(conf_upstream_interface.ifr_name, value, IFNAMSIZ - 1); 368 | conf_upstream_interface.ifr_ifindex = if_nametoindex(conf_upstream_interface.ifr_name); 369 | } 370 | else 371 | { 372 | logger(LOG_INFO, "Warning: Config file value \"upstream-interface\" ignored. It's already set on CmdLine.\n"); 373 | } 374 | return; 375 | } 376 | if (strcasecmp("fcc-nat-traversal", param) == 0) 377 | { 378 | if (!cmd_fcc_nat_traversal_set) 379 | { 380 | conf_fcc_nat_traversal = atoi(value); 381 | } 382 | else 383 | { 384 | logger(LOG_INFO, "Warning: Config file value \"fcc-nat-traversal\" ignored. It's already set on CmdLine.\n"); 385 | } 386 | return; 387 | } 388 | 389 | logger(LOG_ERROR, "Unknown config parameter: %s\n", param); 390 | } 391 | 392 | int parseConfigFile(char *path) 393 | { 394 | FILE *cfile; 395 | char line[MAX_LINE]; 396 | int i, bindMsgDone = 0; 397 | enum section_e section = SEC_NONE; 398 | 399 | logger(LOG_DEBUG, "Opening %s\n", path); 400 | cfile = fopen(path, "r"); 401 | if (cfile == NULL) 402 | return -1; 403 | 404 | while (fgets(line, MAX_LINE, cfile)) 405 | { 406 | i = 0; 407 | 408 | while (isspace(line[i])) 409 | i++; 410 | 411 | if (line[i] == '\0' || line[i] == '#' || 412 | line[i] == ';') 413 | continue; 414 | if (line[i] == '[') 415 | { /* section change */ 416 | char *end = index(line + i, ']'); 417 | if (end) 418 | { 419 | char *secname = strndupa(line + i + 1, end - line - i - 1); 420 | if (strcasecmp("bind", secname) == 0) 421 | { 422 | section = SEC_BIND; 423 | continue; 424 | } 425 | if (strcasecmp("services", secname) == 0) 426 | { 427 | section = SEC_SERVICES; 428 | continue; 429 | } 430 | if (strcasecmp("global", secname) == 0) 431 | { 432 | section = SEC_GLOBAL; 433 | continue; 434 | } 435 | logger(LOG_ERROR, "Invalid section name: %s\n", secname); 436 | continue; 437 | } 438 | else 439 | { 440 | logger(LOG_ERROR, "Unterminated section: %s\n", line + i); 441 | continue; 442 | } 443 | } 444 | 445 | if (cmd_bind_set && section == SEC_BIND) 446 | { 447 | if (!bindMsgDone) 448 | { 449 | logger(LOG_INFO, "Warning: Config file section \"[bind]\" ignored. It's already set on CmdLine.\n"); 450 | bindMsgDone = 1; 451 | } 452 | continue; 453 | } 454 | 455 | switch (section) 456 | { 457 | case SEC_BIND: 458 | parseBindSec(line + i); 459 | break; 460 | case SEC_SERVICES: 461 | parseServicesSec(line + i); 462 | break; 463 | case SEC_GLOBAL: 464 | parseGlobalSec(line + i); 465 | break; 466 | default: 467 | logger(LOG_ERROR, "Unrecognised config line: %s\n", line); 468 | } 469 | } 470 | fclose(cfile); 471 | return 0; 472 | } 473 | 474 | struct bindaddr_s *newEmptyBindaddr() 475 | { 476 | struct bindaddr_s *ba; 477 | ba = malloc(sizeof(struct bindaddr_s)); 478 | memset(ba, 0, sizeof(*ba)); 479 | ba->service = strdup("8080"); 480 | return ba; 481 | } 482 | 483 | void freeBindaddr(struct bindaddr_s *ba) 484 | { 485 | struct bindaddr_s *bat; 486 | while (ba) 487 | { 488 | bat = ba; 489 | ba = ba->next; 490 | if (bat->node) 491 | free(bat->node); 492 | if (bat->service) 493 | free(bat->service); 494 | free(bat); 495 | } 496 | } 497 | 498 | /* Setup configuration defaults */ 499 | void restoreConfDefaults() 500 | { 501 | struct services_s *servtmp; 502 | struct bindaddr_s *bindtmp; 503 | 504 | conf_verbosity = LOG_ERROR; 505 | cmd_verbosity_set = 0; 506 | conf_daemonise = 0; 507 | cmd_daemonise_set = 0; 508 | conf_maxclients = 5; 509 | cmd_maxclients_set = 0; 510 | conf_udpxy = 1; 511 | cmd_udpxy_set = 0; 512 | cmd_bind_set = 0; 513 | conf_fcc_nat_traversal = FCC_NAT_T_DISABLED; 514 | cmd_fcc_nat_traversal_set = 0; 515 | 516 | if (conf_hostname != NULL) 517 | { 518 | free(conf_hostname); 519 | conf_hostname = NULL; 520 | } 521 | cmd_hostname_set = 0; 522 | 523 | if (conf_upstream_interface.ifr_name != NULL) 524 | { 525 | memset(&conf_upstream_interface, 0, sizeof(struct ifreq)); 526 | } 527 | cmd_upstream_interface_set = 0; 528 | 529 | while (services != NULL) 530 | { 531 | servtmp = services; 532 | services = services->next; 533 | if (servtmp->url != NULL) 534 | { 535 | free(servtmp->url); 536 | } 537 | if (servtmp->addr != NULL) 538 | { 539 | freeaddrinfo(servtmp->addr); 540 | } 541 | } 542 | 543 | while (bindaddr != NULL) 544 | { 545 | bindtmp = bindaddr; 546 | bindaddr = bindaddr->next; 547 | if (bindtmp->node != NULL) 548 | free(bindtmp->node); 549 | if (bindtmp->service != NULL) 550 | free(bindtmp->service); 551 | } 552 | } 553 | 554 | void usage(FILE *f, char *progname) 555 | { 556 | char *prog = basename(progname); 557 | fprintf(f, 558 | PACKAGE " - Multicast RTP to Unicast HTTP stream convertor\n" 559 | "\n" 560 | "Version " VERSION "\n" 561 | "Copyright 2008-2014 Ondrej Caletka \n" 562 | "\n" 563 | "This program is free software; you can redistribute it and/or modify\n" 564 | "it under the terms of the GNU General Public License version 2\n" 565 | "as published by the Free Software Foundation.\n"); 566 | fprintf(f, 567 | "\n" 568 | "Usage: %s [options]\n" 569 | "\n" 570 | "Options:\n" 571 | "\t-h --help Show this help\n" 572 | "\t-v --verbose Increase verbosity\n" 573 | "\t-q --quiet Report only fatal errors\n" 574 | "\t-d --daemon Fork to background (implies -q)\n" 575 | "\t-D --nodaemon Do not daemonise. (default)\n" 576 | "\t-U --noudpxy Disable UDPxy compatibility\n" 577 | "\t-m --maxclients Serve max n requests simultaneously (dfl 5)\n" 578 | "\t-l --listen [addr:]port Address/port to bind (default ANY:8080)\n" 579 | "\t-c --config Read this file, instead of\n" 580 | "\t-C --noconfig Do not read the default config\n" 581 | "\t-n --fcc-nat-traversal <0/1/2> NAT traversal for FCC media stream, 0=disabled, 1=punchhole, 2=NAT-PMP (default 0)\n" 582 | "\t-H --hostname Hostname to check in the Host: HTTP header (default none)\n" 583 | "\t-i --upstream-interface Interface to use for requesting upstream media stream (default none, which follows the routing table)\n" 584 | "\t default " CONFIGFILE "\n", 585 | prog); 586 | } 587 | 588 | void parseBindCmd(char *optarg) 589 | { 590 | char *p, *node, *service; 591 | struct bindaddr_s *ba; 592 | 593 | if (optarg[0] == '[') 594 | { 595 | p = index(optarg++, ']'); 596 | if (p) 597 | { 598 | *p = '\0'; 599 | p = rindex(++p, ':'); 600 | } 601 | } 602 | else 603 | { 604 | p = rindex(optarg, ':'); 605 | } 606 | if (p) 607 | { 608 | *p = '\0'; 609 | node = strdup(optarg); 610 | service = strdup(p + 1); 611 | } 612 | else 613 | { 614 | node = NULL; 615 | service = strdup(optarg); 616 | } 617 | 618 | logger(LOG_DEBUG, "node: %s, port: %s\n", node, service); 619 | ba = malloc(sizeof(struct bindaddr_s)); 620 | ba->node = node; 621 | ba->service = service; 622 | ba->next = bindaddr; 623 | bindaddr = ba; 624 | } 625 | 626 | void parseCmdLine(int argc, char *argv[]) 627 | { 628 | const struct option longopts[] = { 629 | {"verbose", required_argument, 0, 'v'}, 630 | {"quiet", no_argument, 0, 'q'}, 631 | {"help", no_argument, 0, 'h'}, 632 | {"daemon", no_argument, 0, 'd'}, 633 | {"nodaemon", no_argument, 0, 'D'}, 634 | {"noudpxy", no_argument, 0, 'U'}, 635 | {"maxclients", required_argument, 0, 'm'}, 636 | {"listen", required_argument, 0, 'l'}, 637 | {"config", required_argument, 0, 'c'}, 638 | {"noconfig", no_argument, 0, 'C'}, 639 | {"fcc-nat-traversal", required_argument, 0, 'n'}, 640 | {"hostname", required_argument, 0, 'H'}, 641 | {"upstream-interface", required_argument, 0, 'i'}, 642 | {0, 0, 0, 0}}; 643 | 644 | const char shortopts[] = "v:qhdDUm:c:l:n:H:i:C"; 645 | int option_index, opt; 646 | int configfile_failed = 1; 647 | 648 | restoreConfDefaults(); 649 | 650 | while ((opt = getopt_long(argc, argv, shortopts, 651 | longopts, &option_index)) != -1) 652 | { 653 | switch (opt) 654 | { 655 | case 0: 656 | break; 657 | case 'v': 658 | conf_verbosity = atoi(optarg); 659 | cmd_verbosity_set = 1; 660 | break; 661 | case 'q': 662 | conf_verbosity = 0; 663 | cmd_verbosity_set = 1; 664 | break; 665 | case 'h': 666 | usage(stdout, argv[0]); 667 | exit(EXIT_SUCCESS); 668 | break; 669 | case 'd': 670 | conf_daemonise = 1; 671 | cmd_daemonise_set = 1; 672 | break; 673 | case 'D': 674 | conf_daemonise = 0; 675 | cmd_daemonise_set = 1; 676 | break; 677 | case 'U': 678 | conf_udpxy = 0; 679 | cmd_udpxy_set = 1; 680 | break; 681 | case 'm': 682 | if (atoi(optarg) < 1) 683 | { 684 | logger(LOG_ERROR, "Invalid maxclients! Ignoring.\n"); 685 | } 686 | else 687 | { 688 | conf_maxclients = atoi(optarg); 689 | cmd_maxclients_set = 1; 690 | } 691 | break; 692 | case 'c': 693 | configfile_failed = parseConfigFile(optarg); 694 | break; 695 | case 'C': 696 | configfile_failed = 0; 697 | break; 698 | case 'l': 699 | parseBindCmd(optarg); 700 | cmd_bind_set = 1; 701 | break; 702 | case 'n': 703 | conf_fcc_nat_traversal = atoi(optarg); 704 | cmd_fcc_nat_traversal_set = 1; 705 | break; 706 | case 'H': 707 | conf_hostname = strdup(optarg); 708 | cmd_hostname_set = 1; 709 | break; 710 | case 'i': 711 | strncpy(conf_upstream_interface.ifr_name, optarg, IFNAMSIZ - 1); 712 | conf_upstream_interface.ifr_ifindex = if_nametoindex(conf_upstream_interface.ifr_name); 713 | cmd_upstream_interface_set = 1; 714 | break; 715 | default: 716 | logger(LOG_FATAL, "Unknown option! %d \n", opt); 717 | usage(stderr, argv[0]); 718 | exit(EXIT_FAILURE); 719 | } 720 | } 721 | if (configfile_failed) 722 | { 723 | configfile_failed = parseConfigFile(CONFIGFILE); 724 | } 725 | if (configfile_failed) 726 | { 727 | logger(LOG_INFO, "Warning: No configfile found.\n"); 728 | } 729 | logger(LOG_DEBUG, "Verbosity: %d, Daemonise: %d, Maxclients: %d\n", 730 | conf_verbosity, conf_daemonise, conf_maxclients); 731 | } 732 | -------------------------------------------------------------------------------- /src/httpclients.c: -------------------------------------------------------------------------------- 1 | /* 2 | * RTP2HTTP Proxy - Multicast RTP stream to UNICAST HTTP translator 3 | * 4 | * Copyright (C) 2008-2010 Ondrej Caletka 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License version 2 8 | * as published by the Free Software Foundation. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program (see the file COPYING included with this 17 | * distribution); if not, write to the Free Software Foundation, Inc., 18 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #define _GNU_SOURCE 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "rtp2httpd.h" 36 | 37 | #ifdef HAVE_CONFIG_H 38 | #include "config.h" 39 | #endif /* HAVE_CONFIG_H */ 40 | 41 | #define BUFLEN 100 42 | #define UDPBUFLEN 2000 43 | 44 | static const char unimplemented[] = 45 | "\r\n" 46 | "\r\n" 47 | "501 Method Not Implemented\r\n" 48 | "\r\n" 49 | "

501 Method Not Implemented

\r\n" 50 | "

Sorry, only GET is supported.

\r\n" 51 | "
\r\n" 52 | "
Server " PACKAGE " version " VERSION "
\r\n" 53 | "\r\n"; 54 | 55 | static const char badrequest[] = 56 | "\r\n" 57 | "\r\n" 58 | "400 Bad Request\r\n" 59 | "\r\n" 60 | "

400 Bad Request

\r\n" 61 | "

Your browser sent a request that this server could not understand.
\r\n" 62 | "

\r\n" 63 | "
\r\n" 64 | "
Server " PACKAGE " version " VERSION "
\r\n" 65 | "\r\n"; 66 | 67 | static const char serviceNotFound[] = 68 | "\r\n" 69 | "\r\n" 70 | "404 Service not found!\r\n" 71 | "\r\n" 72 | "

404 Service not found!

\r\n" 73 | "

Sorry, this service was not configured.

\r\n" 74 | "
\r\n" 75 | "
Server " PACKAGE " version " VERSION "
\r\n" 76 | "\r\n"; 77 | 78 | static const char serviceUnavailable[] = 79 | "\r\n" 80 | "\r\n" 81 | "503 Service Unavaliable\r\n" 82 | "\r\n" 83 | "

503 Service Unavaliable

\r\n" 84 | "

Sorry, there are too many connections at this time.\r\n" 85 | "Try again later.

\r\n" 86 | "
\r\n" 87 | "
Server " PACKAGE " version " VERSION "
\r\n" 88 | "\r\n"; 89 | 90 | static const char *responseCodes[] = { 91 | "HTTP/1.1 200 OK\r\n", /* 0 */ 92 | "HTTP/1.1 404 Not Found\r\n", /* 1 */ 93 | "HTTP/1.1 400 Bad Request\r\n", /* 2 */ 94 | "HTTP/1.1 501 Not Implemented\r\n", /* 3 */ 95 | "HTTP/1.1 503 Service Unavailable\r\n", /* 4 */ 96 | }; 97 | 98 | #define STATUS_200 0 99 | #define STATUS_404 1 100 | #define STATUS_400 2 101 | #define STATUS_501 3 102 | #define STATUS_503 4 103 | 104 | static const char *contentTypes[] = { 105 | "Content-Type: application/octet-stream\r\n", /* 0 */ 106 | "Content-Type: text/html\r\n", /* 1 */ 107 | "Content-Type: text/html; charset=utf-8\r\n", /* 2 */ 108 | "Content-Type: video/mpeg\r\n", /* 3 */ 109 | "Content-Type: audio/mpeg\r\n", /* 4 */ 110 | }; 111 | 112 | #define CONTENT_OSTREAM 0 113 | #define CONTENT_HTML 1 114 | #define CONTENT_HTMLUTF 2 115 | #define CONTENT_MPEGV 3 116 | #define CONTENT_MPEGA 4 117 | 118 | static const char staticHeaders[] = 119 | "Server: " PACKAGE "/" VERSION "\r\n" 120 | "\r\n"; 121 | 122 | /* 123 | * Linked list of allowed services 124 | */ 125 | 126 | struct services_s *services = NULL; 127 | 128 | #define RECV_STATE_INIT 0 129 | #define RECV_STATE_FCC_REQUESTED 1 130 | #define RECV_STATE_MCAST_REQUESTED 2 131 | #define RECV_STATE_MCAST_ACCEPTED 3 132 | 133 | #define FCC_PK_LEN_REQ 40 134 | #define FCC_PK_LEN_TERM 16 135 | 136 | /* 137 | * Ensures that all data are written to the socket 138 | */ 139 | static void writeToClient(int s, const uint8_t *buf, const size_t buflen) 140 | { 141 | ssize_t actual; 142 | size_t written = 0; 143 | while (written < buflen) 144 | { 145 | actual = write(s, buf + written, buflen - written); 146 | if (actual <= 0) 147 | { 148 | exit(RETVAL_WRITE_FAILED); 149 | } 150 | written += actual; 151 | } 152 | } 153 | 154 | /* 155 | * Send a HTTP/1.x response header 156 | * @params s socket 157 | * @params status index to responseCodes[] array 158 | * @params type index to contentTypes[] array 159 | */ 160 | static void headers(int s, int status, int type) 161 | { 162 | writeToClient(s, (uint8_t *)responseCodes[status], 163 | strlen(responseCodes[status])); 164 | writeToClient(s, (uint8_t *)contentTypes[type], 165 | strlen(contentTypes[type])); 166 | writeToClient(s, (uint8_t *)staticHeaders, 167 | sizeof(staticHeaders) - 1); 168 | } 169 | 170 | void sigpipe_handler(int signum) 171 | { 172 | exit(RETVAL_WRITE_FAILED); 173 | } 174 | 175 | /** 176 | * Parses URL in UDPxy format, i.e. /rtp/:port 177 | * returns a pointer to statically alocated service struct if success, 178 | * NULL otherwise. 179 | */ 180 | 181 | static struct services_s *udpxy_parse(char *url) 182 | { 183 | static struct services_s serv; 184 | static struct addrinfo res_ai, msrc_res_ai, fcc_res_ai; 185 | static struct sockaddr_storage res_addr, msrc_res_addr, fcc_res_addr; 186 | 187 | char *addrstr, *portstr, *msrc = "", *msaddr = "", *msport = "", *fccaddr, *fccport; 188 | int i, r, rr, rrr; 189 | char c; 190 | struct addrinfo hints, *res, *msrc_res, *fcc_res; 191 | 192 | if (strncmp("/rtp/", url, 5) == 0) 193 | serv.service_type = SERVICE_MRTP; 194 | else if (strncmp("/udp/", url, 5) == 0) 195 | serv.service_type = SERVICE_MUDP; 196 | else 197 | return NULL; 198 | addrstr = rindex(url, '/'); 199 | if (!addrstr) 200 | return NULL; 201 | /* Decode URL encoded strings */ 202 | for (i = 0; i < (strlen(addrstr) - 2); i++) 203 | { 204 | if (addrstr[i] == '%' && 205 | sscanf(addrstr + i + 1, "%2hhx", (unsigned char *)&c) > 0) 206 | { 207 | addrstr[i] = c; 208 | memmove(addrstr + i + 1, addrstr + i + 3, 1 + strlen(addrstr + i + 3)); 209 | } 210 | } 211 | logger(LOG_DEBUG, "decoded addr: %s\n", addrstr); 212 | fccaddr = rindex(addrstr, '?'); 213 | if (fccaddr) 214 | { 215 | *fccaddr = '\0'; 216 | fccaddr++; 217 | fccaddr = strcasestr(fccaddr, "fcc="); 218 | if (fccaddr) 219 | { 220 | fccaddr += 4; 221 | fccport = rindex(fccaddr, ':'); 222 | if (fccport) 223 | { 224 | *fccport = '\0'; 225 | fccport++; 226 | } 227 | } 228 | } 229 | else 230 | { 231 | fccaddr = ""; 232 | } 233 | if (!fccport) 234 | { 235 | fccport = ""; 236 | } 237 | if (addrstr[1] == '[') 238 | { 239 | portstr = index(addrstr, ']'); 240 | addrstr += 2; 241 | if (portstr) 242 | { 243 | *portstr = '\0'; 244 | portstr = rindex(++portstr, ':'); 245 | } 246 | } 247 | else 248 | { 249 | portstr = rindex(addrstr++, ':'); 250 | } 251 | 252 | if (strstr(addrstr, "@") != NULL) 253 | { 254 | char *split; 255 | char *current; 256 | int cnt = 0; 257 | split = strtok(addrstr, "@"); 258 | while (split != NULL) 259 | { 260 | current = split; 261 | if (cnt == 0) 262 | msrc = current; 263 | split = strtok(NULL, "@"); 264 | if (cnt > 0 && split != NULL) 265 | { 266 | strcat(msrc, "@"); 267 | strcat(msrc, current); 268 | } 269 | if (cnt > 0 && split == NULL) 270 | addrstr = current; 271 | cnt++; 272 | } 273 | 274 | cnt = 0; 275 | msaddr = msrc; 276 | split = strtok(msrc, ":"); 277 | while (split != NULL) 278 | { 279 | current = split; 280 | if (cnt == 0) 281 | msaddr = current; 282 | split = strtok(NULL, ":"); 283 | if (cnt > 0 && split != NULL) 284 | { 285 | strcat(msaddr, ":"); 286 | strcat(msaddr, current); 287 | } 288 | if (cnt > 0 && split == NULL) 289 | msport = current; 290 | cnt++; 291 | } 292 | } 293 | 294 | if (portstr) 295 | { 296 | *portstr = '\0'; 297 | portstr++; 298 | } 299 | else 300 | portstr = "1234"; 301 | 302 | logger(LOG_DEBUG, "addrstr: %s portstr: %s msrc: %s fccaddr: %s fccport: %s\n", addrstr, portstr, msrc, fccaddr, fccport); 303 | 304 | memset(&hints, 0, sizeof(hints)); 305 | hints.ai_socktype = SOCK_DGRAM; 306 | r = getaddrinfo(addrstr, portstr, &hints, &res); 307 | rr = 0; 308 | rrr = 0; 309 | if (strcmp(msrc, "") != 0 && msrc != NULL) 310 | { 311 | rr = getaddrinfo(msrc, 0, &hints, &msrc_res); 312 | } 313 | if (strcmp(fccaddr, "") != 0 && fccaddr != NULL) 314 | { 315 | rrr = getaddrinfo(fccaddr, fccport, &hints, &fcc_res); 316 | } 317 | if (r | rr | rrr) 318 | { 319 | if (r) 320 | { 321 | logger(LOG_ERROR, "Cannot resolve Multicast address. GAI: %s\n", 322 | gai_strerror(r)); 323 | } 324 | if (rr) 325 | { 326 | logger(LOG_ERROR, "Cannot resolve Multicast source address. GAI: %s\n", 327 | gai_strerror(rr)); 328 | } 329 | if (rrr) 330 | { 331 | logger(LOG_ERROR, "Cannot resolve FCC server address. GAI: %s\n", 332 | gai_strerror(rrr)); 333 | } 334 | 335 | free(msrc); 336 | msrc = NULL; 337 | return NULL; 338 | } 339 | if (res->ai_next != NULL) 340 | { 341 | logger(LOG_ERROR, "Warning: maddr is ambiguos.\n"); 342 | } 343 | if (strcmp(msrc, "") != 0 && msrc != NULL) 344 | { 345 | if (msrc_res->ai_next != NULL) 346 | { 347 | logger(LOG_ERROR, "Warning: msrc is ambiguos.\n"); 348 | } 349 | } 350 | if (strcmp(fccaddr, "") != 0 && fccaddr != NULL) 351 | { 352 | if (fcc_res->ai_next != NULL) 353 | { 354 | logger(LOG_ERROR, "Warning: fcc is ambiguos.\n"); 355 | } 356 | } 357 | 358 | /* Copy result into statically allocated structs */ 359 | memcpy(&res_addr, res->ai_addr, res->ai_addrlen); 360 | memcpy(&res_ai, res, sizeof(struct addrinfo)); 361 | res_ai.ai_addr = (struct sockaddr *)&res_addr; 362 | res_ai.ai_canonname = NULL; 363 | res_ai.ai_next = NULL; 364 | serv.addr = &res_ai; 365 | 366 | serv.msrc_addr = NULL; 367 | if (strcmp(msrc, "") != 0 && msrc != NULL) 368 | { 369 | /* Copy result into statically allocated structs */ 370 | memcpy(&msrc_res_addr, msrc_res->ai_addr, msrc_res->ai_addrlen); 371 | memcpy(&msrc_res_ai, msrc_res, sizeof(struct addrinfo)); 372 | msrc_res_ai.ai_addr = (struct sockaddr *)&msrc_res_addr; 373 | msrc_res_ai.ai_canonname = NULL; 374 | msrc_res_ai.ai_next = NULL; 375 | serv.msrc_addr = &msrc_res_ai; 376 | } 377 | 378 | serv.msrc = strdup(msrc); 379 | 380 | serv.fcc_addr = NULL; 381 | if (strcmp(fccaddr, "") != 0 && fccaddr != NULL) 382 | { 383 | /* Copy result into statically allocated structs */ 384 | memcpy(&fcc_res_addr, fcc_res->ai_addr, fcc_res->ai_addrlen); 385 | memcpy(&fcc_res_ai, fcc_res, sizeof(struct addrinfo)); 386 | fcc_res_ai.ai_addr = (struct sockaddr *)&fcc_res_addr; 387 | fcc_res_ai.ai_canonname = NULL; 388 | fcc_res_ai.ai_next = NULL; 389 | serv.fcc_addr = &fcc_res_ai; 390 | } 391 | 392 | return &serv; 393 | } 394 | 395 | static void bind_to_upstream_interface(int sock) 396 | { 397 | if (conf_upstream_interface.ifr_name != NULL) 398 | { 399 | if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&conf_upstream_interface, sizeof(struct ifreq)) < 0) 400 | { 401 | logger(LOG_ERROR, "Failed to bind to upstream interface %s: %s\n", conf_upstream_interface.ifr_name, strerror(errno)); 402 | } 403 | } 404 | } 405 | 406 | static int join_mcast_group(struct services_s *service) 407 | { 408 | struct group_req gr; 409 | struct group_source_req gsr; 410 | int sock, r, level; 411 | int on = 1; 412 | 413 | sock = socket(service->addr->ai_family, service->addr->ai_socktype, 414 | service->addr->ai_protocol); 415 | r = setsockopt(sock, SOL_SOCKET, 416 | SO_REUSEADDR, &on, sizeof(on)); 417 | if (r) 418 | { 419 | logger(LOG_ERROR, "SO_REUSEADDR " 420 | "failed: %s\n", 421 | strerror(errno)); 422 | } 423 | 424 | r = bind(sock, (struct sockaddr *)service->addr->ai_addr, service->addr->ai_addrlen); 425 | if (r) 426 | { 427 | logger(LOG_ERROR, "Cannot bind: %s\n", 428 | strerror(errno)); 429 | exit(RETVAL_RTP_FAILED); 430 | } 431 | 432 | memcpy(&(gr.gr_group), service->addr->ai_addr, service->addr->ai_addrlen); 433 | 434 | switch (service->addr->ai_family) 435 | { 436 | case AF_INET: 437 | level = SOL_IP; 438 | gr.gr_interface = 0; 439 | break; 440 | 441 | case AF_INET6: 442 | level = SOL_IPV6; 443 | gr.gr_interface = ((const struct sockaddr_in6 *)(service->addr->ai_addr))->sin6_scope_id; 444 | break; 445 | default: 446 | logger(LOG_ERROR, "Address family don't support mcast.\n"); 447 | exit(RETVAL_SOCK_READ_FAILED); 448 | } 449 | 450 | if (conf_upstream_interface.ifr_name != NULL) 451 | { 452 | gr.gr_interface = conf_upstream_interface.ifr_ifindex; 453 | } 454 | 455 | if (strcmp(service->msrc, "") != 0 && service->msrc != NULL) 456 | { 457 | gsr.gsr_group = gr.gr_group; 458 | gsr.gsr_interface = gr.gr_interface; 459 | memcpy(&(gsr.gsr_source), service->msrc_addr->ai_addr, service->msrc_addr->ai_addrlen); 460 | r = setsockopt(sock, level, 461 | MCAST_JOIN_SOURCE_GROUP, &gsr, sizeof(gsr)); 462 | } 463 | else 464 | { 465 | r = setsockopt(sock, level, 466 | MCAST_JOIN_GROUP, &gr, sizeof(gr)); 467 | } 468 | 469 | if (r) 470 | { 471 | logger(LOG_ERROR, "Cannot join mcast group: %s\n", 472 | strerror(errno)); 473 | exit(RETVAL_RTP_FAILED); 474 | } 475 | 476 | return sock; 477 | } 478 | 479 | static uint8_t *build_fcc_request_pk(struct addrinfo *maddr, uint16_t fcc_client_nport) 480 | { 481 | struct sockaddr_in *maddr_sin = (struct sockaddr_in *) 482 | maddr->ai_addr; 483 | 484 | static uint8_t pk[FCC_PK_LEN_REQ]; 485 | memset(&pk, 0, sizeof(pk)); 486 | uint8_t *p = pk; 487 | *(p++) = 0x82; // Version 2, Padding 0, FMT 2 488 | *(p++) = 205; // Type: Generic RTP Feedback (205) 489 | *(uint16_t *)p = htons(sizeof(pk) / 4 - 1); // Length 490 | p += 2; 491 | p += 4; // Sender SSRC 492 | *(uint32_t *)p = maddr_sin->sin_addr.s_addr; // Media source SSRC 493 | p += 4; 494 | 495 | // FCI 496 | p += 4; // Version 0, Reserved 3 bytes 497 | *(uint16_t *)p = fcc_client_nport; // FCC client port 498 | p += 2; 499 | *(uint16_t *)p = maddr_sin->sin_port; // Mcast group port 500 | p += 2; 501 | *(uint32_t *)p = maddr_sin->sin_addr.s_addr; // Mcast group IP 502 | p += 4; 503 | 504 | return pk; 505 | } 506 | 507 | static int get_gw_ip(in_addr_t *addr) 508 | { 509 | long destination, gateway; 510 | char buf[4096]; 511 | FILE *file; 512 | 513 | memset(buf, 0, sizeof(buf)); 514 | 515 | file = fopen("/proc/net/route", "r"); 516 | if (!file) 517 | { 518 | return -1; 519 | } 520 | 521 | while (fgets(buf, sizeof(buf), file)) 522 | { 523 | if (sscanf(buf, "%*s %lx %lx", &destination, &gateway) == 2) 524 | { 525 | if (destination == 0) 526 | { /* default */ 527 | *addr = gateway; 528 | fclose(file); 529 | return 0; 530 | } 531 | } 532 | } 533 | 534 | /* default route not found */ 535 | if (file) 536 | fclose(file); 537 | return -1; 538 | } 539 | 540 | static uint16_t nat_pmp(uint16_t nport, uint32_t lifetime) 541 | { 542 | struct sockaddr_in gw_addr = {.sin_family = AF_INET, .sin_port = htons(5351)}; 543 | uint8_t pk[12]; 544 | uint8_t buf[16]; 545 | struct timeval tv = {.tv_sec = 1, .tv_usec = 0}; 546 | 547 | if (get_gw_ip(&gw_addr.sin_addr.s_addr) < 0) 548 | return 0; 549 | int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 550 | bind_to_upstream_interface(sock); 551 | setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof tv); 552 | pk[0] = 0; // Version 553 | pk[1] = 1; // UDP 554 | *(uint16_t *)(pk + 2) = 0; // Reserved 555 | *(uint16_t *)(pk + 4) = nport; // Private port 556 | *(uint16_t *)(pk + 6) = 0; // Public port 557 | *(uint32_t *)(pk + 8) = htonl(lifetime); 558 | sendto(sock, pk, sizeof(pk), 0, (struct sockaddr *)&gw_addr, sizeof(gw_addr)); 559 | if (recv(sock, buf, sizeof(buf), 0) > 0) 560 | { 561 | if (*(uint16_t *)(buf + 2) == 0) 562 | { // Result code 563 | close(sock); 564 | return *(uint16_t *)(buf + 10); // Mapped public port 565 | } 566 | } 567 | close(sock); 568 | return 0; 569 | } 570 | 571 | static uint8_t *build_fcc_term_pk(struct addrinfo *maddr, uint16_t seqn) 572 | { 573 | struct sockaddr_in *maddr_sin = (struct sockaddr_in *)maddr->ai_addr; 574 | 575 | static uint8_t pk[FCC_PK_LEN_TERM]; 576 | memset(&pk, 0, sizeof(pk)); 577 | uint8_t *p = pk; 578 | *(p++) = 0x85; // Version 2, Padding 0, FMT 5 579 | *(p++) = 205; // Type: Generic RTP Feedback (205) 580 | *(uint16_t *)p = htons(sizeof(pk) / 4 - 1); // Length 581 | p += 2; 582 | p += 4; // Sender SSRC 583 | *(uint32_t *)p = maddr_sin->sin_addr.s_addr; // Media source SSRC 584 | p += 4; 585 | 586 | // FCI 587 | *(p++) = seqn ? 0 : 1; // Stop bit, 0 = normal, 1 = force 588 | p++; // Reserved 589 | *(uint16_t *)p = htons(seqn); // First multicast packet sequence 590 | p += 2; 591 | 592 | return pk; 593 | } 594 | 595 | static int get_rtp_payload(uint8_t *buf, int recv_len, uint8_t **payload, int *size) 596 | { 597 | int payloadstart, payloadlength; 598 | 599 | if (recv_len < 12 || (buf[0] & 0xC0) != 0x80) 600 | { 601 | /*malformed RTP/UDP/IP packet*/ 602 | logger(LOG_DEBUG, "Malformed RTP packet received\n"); 603 | return -1; 604 | } 605 | 606 | payloadstart = 12; /* basic RTP header length */ 607 | payloadstart += (buf[0] & 0x0F) * 4; /*CRSC headers*/ 608 | if (buf[0] & 0x10) 609 | { /*Extension header*/ 610 | payloadstart += 4 + 4 * ntohs(*((uint16_t *)(buf + payloadstart + 2))); 611 | } 612 | payloadlength = recv_len - payloadstart; 613 | if (buf[0] & 0x20) 614 | { /*Padding*/ 615 | payloadlength -= buf[recv_len]; 616 | /*last octet indicate padding length*/ 617 | } 618 | if (payloadlength < 0) 619 | { 620 | logger(LOG_DEBUG, "Malformed RTP packet received\n"); 621 | return -1; 622 | } 623 | 624 | *payload = buf + payloadstart; 625 | *size = payloadlength; 626 | return 0; 627 | } 628 | 629 | static void write_rtp_payload_to_client(int client, int recv_len, uint8_t *buf, uint16_t *oldseqn, uint16_t *notfirst) 630 | { 631 | int payloadlength; 632 | uint8_t *payload; 633 | uint16_t seqn; 634 | 635 | get_rtp_payload(buf, recv_len, &payload, &payloadlength); 636 | 637 | seqn = ntohs(*(uint16_t *)(buf + 2)); 638 | if (*notfirst && seqn == *oldseqn) 639 | { 640 | logger(LOG_DEBUG, "Duplicated RTP packet " 641 | "received (seqn %d)\n", 642 | seqn); 643 | return; 644 | } 645 | if (*notfirst && (seqn != ((*oldseqn + 1) & 0xFFFF))) 646 | { 647 | logger(LOG_DEBUG, "Congestion - expected %d, " 648 | "received %d\n", 649 | (*oldseqn + 1) & 0xFFFF, seqn); 650 | } 651 | *oldseqn = seqn; 652 | *notfirst = 1; 653 | 654 | writeToClient(client, payload, payloadlength); 655 | } 656 | 657 | static ssize_t sendto_triple(int __fd, const void *__buf, size_t __n, 658 | int __flags, struct sockaddr_in *__addr, socklen_t __addr_len) 659 | { 660 | static uint8_t i; 661 | for (i = 0; i < 3; i++) 662 | { 663 | if (sendto(__fd, __buf, __n, __flags, (struct sockaddr *)__addr, __addr_len) < 0) 664 | { 665 | return -1; 666 | } 667 | } 668 | return __n; 669 | } 670 | 671 | static void fcc_cleanup(int fcc_sock, struct sockaddr_in *fcc_server, struct services_s *service, uint16_t mapped_pub_port, struct sockaddr_in *fcc_client) 672 | { 673 | if (fcc_sock) 674 | sendto_triple(fcc_sock, build_fcc_term_pk(service->addr, 0), FCC_PK_LEN_TERM, 0, fcc_server, sizeof(*fcc_server)); 675 | if (mapped_pub_port) 676 | nat_pmp(fcc_client->sin_port, 0); 677 | } 678 | 679 | static void startRTPstream(int client, struct services_s *service) 680 | { 681 | int recv_state = RECV_STATE_INIT; 682 | int mcast_sock = 0, fcc_sock = 0, max_sock; 683 | int r; 684 | struct sockaddr_in *fcc_server; 685 | struct sockaddr_in fcc_client; 686 | struct sockaddr_in peer_addr; 687 | socklen_t slen = sizeof(peer_addr); 688 | uint8_t buf[UDPBUFLEN]; 689 | uint8_t *mcast_pending_buf = NULL, *mcast_pbuf_c, *rtp_payload, mcast_pbuf_full; 690 | uint mcast_pbuf_len; 691 | int actualr; 692 | uint16_t mapped_pub_port = 0, media_port = 0, seqn, mcast_pbuf_lsqen, notfirst = 0, fcc_term_sent = 0, fcc_term_seqn = 0; 693 | int payloadlength; 694 | fd_set rfds; 695 | struct timeval timeout; 696 | 697 | void sig_handler(int signum) 698 | { 699 | fcc_cleanup(fcc_sock, fcc_server, service, mapped_pub_port, &fcc_client); 700 | if (signum) 701 | exit(RETVAL_CLEAN); 702 | } 703 | 704 | void exit_handler() 705 | { 706 | sig_handler(0); 707 | } 708 | 709 | atexit(exit_handler); 710 | signal(SIGTERM, sig_handler); 711 | signal(SIGINT, sig_handler); 712 | signal(SIGPIPE, sig_handler); 713 | 714 | while (1) 715 | { 716 | if (recv_state == RECV_STATE_INIT) 717 | { 718 | if (service->service_type == SERVICE_MRTP && service->fcc_addr) 719 | { 720 | struct sockaddr_in sin; 721 | if (!fcc_sock) 722 | { 723 | fcc_sock = socket(AF_INET, service->fcc_addr->ai_socktype, service->fcc_addr->ai_protocol); 724 | bind_to_upstream_interface(fcc_sock); 725 | sin.sin_family = AF_INET; 726 | sin.sin_addr.s_addr = INADDR_ANY; 727 | sin.sin_port = 0; 728 | r = bind(fcc_sock, (struct sockaddr *)&sin, sizeof(sin)); 729 | if (r) 730 | { 731 | logger(LOG_ERROR, "Cannot bind: %s\n", strerror(errno)); 732 | exit(RETVAL_RTP_FAILED); 733 | } 734 | slen = sizeof(fcc_client); 735 | getsockname(fcc_sock, (struct sockaddr *)&fcc_client, &slen); 736 | if (conf_fcc_nat_traversal == FCC_NAT_T_NAT_PMP) 737 | { 738 | mapped_pub_port = nat_pmp(fcc_client.sin_port, 86400); 739 | logger(LOG_DEBUG, "NAT PMP result: %u\n", ntohs(mapped_pub_port)); 740 | } 741 | fcc_server = (struct sockaddr_in *)service->fcc_addr->ai_addr; 742 | } 743 | r = sendto_triple(fcc_sock, build_fcc_request_pk(service->addr, mapped_pub_port ? mapped_pub_port : fcc_client.sin_port), FCC_PK_LEN_REQ, 0, fcc_server, sizeof(*fcc_server)); 744 | if (r < 0) 745 | { 746 | logger(LOG_ERROR, "Unable to send FCC req message: %s\n", strerror(errno)); 747 | exit(RETVAL_RTP_FAILED); 748 | } 749 | logger(LOG_DEBUG, "FCC server requested.\n"); 750 | recv_state = RECV_STATE_FCC_REQUESTED; 751 | } 752 | else 753 | { 754 | mcast_sock = join_mcast_group(service); 755 | recv_state = RECV_STATE_MCAST_ACCEPTED; 756 | } 757 | } 758 | else 759 | { 760 | FD_ZERO(&rfds); 761 | max_sock = client; 762 | FD_SET(client, &rfds); /* Will be set if connection to client lost.*/ 763 | if (fcc_sock && recv_state != RECV_STATE_MCAST_ACCEPTED) 764 | { 765 | FD_SET(fcc_sock, &rfds); 766 | if (fcc_sock > max_sock) 767 | max_sock = fcc_sock; 768 | } 769 | if (mcast_sock && recv_state != RECV_STATE_FCC_REQUESTED) 770 | { 771 | FD_SET(mcast_sock, &rfds); 772 | if (mcast_sock > max_sock) 773 | max_sock = mcast_sock; 774 | } 775 | timeout.tv_sec = 5; 776 | timeout.tv_usec = 0; 777 | 778 | /* We use select to get rid of recv stuck if 779 | * multicast group is unoperated. 780 | */ 781 | r = select(max_sock + 1, &rfds, NULL, NULL, &timeout); 782 | if (r < 0 && errno == EINTR) 783 | { 784 | continue; 785 | } 786 | if (r == 0) 787 | { /* timeout reached */ 788 | exit(RETVAL_SOCK_READ_FAILED); 789 | } 790 | if (FD_ISSET(client, &rfds)) 791 | { /* client written stg, or conn. lost */ 792 | exit(RETVAL_WRITE_FAILED); 793 | } 794 | else if (fcc_sock && FD_ISSET(fcc_sock, &rfds)) 795 | { 796 | actualr = recvfrom(fcc_sock, buf, sizeof(buf), 0, (struct sockaddr *)&peer_addr, &slen); 797 | if (actualr < 0) 798 | { 799 | logger(LOG_ERROR, "FCC recv failed: %s\n", strerror(errno)); 800 | continue; 801 | } 802 | if (peer_addr.sin_addr.s_addr != fcc_server->sin_addr.s_addr) 803 | { 804 | continue; 805 | } 806 | if (peer_addr.sin_port == fcc_server->sin_port) 807 | { // RTCP signal command 808 | if (buf[1] != 205) 809 | { 810 | logger(LOG_DEBUG, "Unrecognized FCC payload type: %u\n", buf[1]); 811 | continue; 812 | } 813 | if (buf[0] == 0x83) 814 | { // FMT 3 815 | if (buf[12] != 0) 816 | { // Result not success 817 | logger(LOG_DEBUG, "FCC (FMT 3) gives an error result code: %u\n", buf[12]); 818 | mcast_sock = join_mcast_group(service); 819 | recv_state = RECV_STATE_MCAST_ACCEPTED; 820 | continue; 821 | } 822 | uint16_t new_signal_port = *(uint16_t *)(buf + 14); 823 | int signal_port_changed = 0, media_port_changed = 0; 824 | if (new_signal_port && new_signal_port != fcc_server->sin_port) 825 | { 826 | logger(LOG_DEBUG, "FCC (FMT 3) gives a new signal port: %u\n", ntohs(new_signal_port)); 827 | fcc_server->sin_port = new_signal_port; 828 | signal_port_changed = 1; 829 | } 830 | uint16_t new_media_port = *(uint16_t *)(buf + 16); 831 | if (new_media_port && new_media_port != media_port) 832 | { 833 | media_port = new_media_port; 834 | logger(LOG_DEBUG, "FCC (FMT 3) gives a new media port: %u\n", ntohs(new_media_port)); 835 | media_port_changed = 1; 836 | } 837 | uint32_t new_fcc_ip = *(uint32_t *)(buf + 20); 838 | if (new_fcc_ip && new_fcc_ip != fcc_server->sin_addr.s_addr) 839 | { 840 | fcc_server->sin_addr.s_addr = new_fcc_ip; 841 | logger(LOG_DEBUG, "FCC (FMT 3) gives a new FCC ip: %s\n", inet_ntoa(fcc_server->sin_addr)); 842 | signal_port_changed = 1; 843 | media_port_changed = 1; 844 | } 845 | if (buf[13] == 3) 846 | { // Redirect to new FCC server 847 | logger(LOG_DEBUG, "FCC (FMT 3) requests a redirection to a new server\n"); 848 | recv_state = RECV_STATE_INIT; 849 | } 850 | else if (buf[13] != 2) 851 | { // Join mcast group instantly 852 | logger(LOG_DEBUG, "FCC (FMT 3) requests immediate mcast join, code: %u\n", buf[13]); 853 | mcast_sock = join_mcast_group(service); 854 | recv_state = RECV_STATE_MCAST_ACCEPTED; 855 | } 856 | else 857 | { 858 | // Send empty packet to make NAT happy 859 | if (conf_fcc_nat_traversal == FCC_NAT_T_PUNCHHOLE) 860 | { 861 | if (media_port_changed && media_port) 862 | { 863 | struct sockaddr_in sintmp = *fcc_server; 864 | sintmp.sin_port = media_port; 865 | sendto_triple(fcc_sock, NULL, 0, 0, &sintmp, sizeof(sintmp)); 866 | logger(LOG_DEBUG, "Tried to NAT punch hole for media port %u\n", media_port); 867 | } 868 | if (signal_port_changed) 869 | { 870 | sendto_triple(fcc_sock, NULL, 0, 0, fcc_server, sizeof(*fcc_server)); 871 | logger(LOG_DEBUG, "Tried to setup NAT punch hole for signal port %u\n", fcc_server->sin_port); 872 | } 873 | } 874 | logger(LOG_DEBUG, "FCC server accepted the req.\n"); 875 | } 876 | } 877 | else if (buf[0] == 0x84) 878 | { // FMT 4 879 | logger(LOG_DEBUG, "FCC (FMT 4) indicates we can now join mcast\n"); 880 | mcast_sock = join_mcast_group(service); 881 | recv_state = RECV_STATE_MCAST_REQUESTED; 882 | } 883 | } 884 | else if (peer_addr.sin_port == media_port) 885 | { // RTP media packet 886 | write_rtp_payload_to_client(client, actualr, buf, &seqn, ¬first); 887 | if (fcc_term_sent && seqn >= fcc_term_seqn - 1) 888 | { 889 | recv_state = RECV_STATE_MCAST_ACCEPTED; 890 | } 891 | } 892 | } 893 | else if (mcast_sock && FD_ISSET(mcast_sock, &rfds)) 894 | { 895 | actualr = recv(mcast_sock, buf, sizeof(buf), 0); 896 | if (actualr < 0) 897 | { 898 | logger(LOG_DEBUG, "Mcast recv fail: %s", strerror(errno)); 899 | continue; 900 | } 901 | if (service->service_type == SERVICE_MUDP) 902 | { 903 | writeToClient(client, buf, sizeof(buf)); 904 | continue; 905 | } 906 | if (recv_state == RECV_STATE_MCAST_ACCEPTED) 907 | { 908 | if (mcast_pending_buf) 909 | { 910 | writeToClient(client, mcast_pending_buf, mcast_pbuf_len); 911 | free(mcast_pending_buf); 912 | mcast_pending_buf = NULL; 913 | seqn = mcast_pbuf_lsqen; 914 | logger(LOG_DEBUG, "Flushed mcast pending buffer to client. Term seqn: %u, mcast pending buffer last sqen: %u\n", fcc_term_seqn, mcast_pbuf_lsqen); 915 | } 916 | write_rtp_payload_to_client(client, actualr, buf, &seqn, ¬first); 917 | } 918 | else if (recv_state == RECV_STATE_MCAST_REQUESTED) 919 | { 920 | mcast_pbuf_lsqen = ntohs(*(uint16_t *)(buf + 2)); 921 | if (!fcc_term_sent) 922 | { 923 | fcc_term_seqn = mcast_pbuf_lsqen; 924 | r = sendto_triple(fcc_sock, build_fcc_term_pk(service->addr, fcc_term_seqn + 2), FCC_PK_LEN_TERM, 0, fcc_server, sizeof(*fcc_server)); 925 | if (r < 0) 926 | { 927 | logger(LOG_ERROR, "Unable to send FCC termination message: %s\n", strerror(errno)); 928 | } 929 | mcast_pbuf_len = (max(fcc_term_seqn - seqn, 10) + 10) * 2000; 930 | mcast_pending_buf = malloc(mcast_pbuf_len); 931 | mcast_pbuf_c = mcast_pending_buf; 932 | mcast_pbuf_full = 0; 933 | fcc_term_sent = 1; 934 | logger(LOG_DEBUG, "FCC term message sent. Mcast pending buffer size: %u\n", mcast_pbuf_len); 935 | } 936 | if (mcast_pbuf_full) 937 | continue; 938 | if (get_rtp_payload(buf, actualr, &rtp_payload, &payloadlength) < 0) 939 | { 940 | continue; 941 | } 942 | if (mcast_pbuf_c + payloadlength > mcast_pending_buf + mcast_pbuf_len) 943 | { 944 | logger(LOG_ERROR, "Mcast pending buffer is full, video quality may suffer.\n", strerror(errno)); 945 | mcast_pbuf_full = 1; 946 | continue; 947 | } 948 | memcpy(mcast_pbuf_c, rtp_payload, payloadlength); 949 | mcast_pbuf_c += payloadlength; 950 | } 951 | } 952 | } 953 | } 954 | 955 | /*SHOULD NEVER REACH THIS*/ 956 | return; 957 | } 958 | 959 | /* 960 | * Service for connected client. 961 | * Run in forked thread. 962 | */ 963 | void clientService(int s) 964 | { 965 | char buf[BUFLEN]; 966 | FILE *client; 967 | int numfields; 968 | char *method, *url, httpver; 969 | char *hostname; 970 | char *urlfrom; 971 | struct services_s *servi; 972 | 973 | signal(SIGPIPE, &sigpipe_handler); 974 | 975 | client = fdopen(s, "r"); 976 | /*read only one line*/ 977 | if (fgets(buf, sizeof(buf), client) == NULL) 978 | { 979 | exit(RETVAL_READ_FAILED); 980 | } 981 | numfields = sscanf(buf, "%ms %ms %c", &method, &url, &httpver); 982 | if (numfields < 2) 983 | { 984 | logger(LOG_DEBUG, "Non-HTTP input.\n"); 985 | } 986 | logger(LOG_INFO, "request: %s %s \n", method, url); 987 | 988 | if (numfields == 3) 989 | { /* Read and discard all headers before replying */ 990 | while (fgets(buf, sizeof(buf), client) != NULL && 991 | strcmp("\r\n", buf) != 0) 992 | { 993 | if (strncasecmp("Host: ", buf, 6) == 0) 994 | { 995 | hostname = strpbrk(buf + 6, ":\r\n"); 996 | if (hostname) 997 | hostname = strndup(buf + 6, hostname - buf - 6); 998 | logger(LOG_DEBUG, "Host header: %s\n", hostname); 999 | } 1000 | } 1001 | } 1002 | 1003 | if (strcmp(method, "GET") != 0 && strcmp(method, "HEAD") != 0) 1004 | { 1005 | if (numfields == 3) 1006 | headers(s, STATUS_501, CONTENT_HTML); 1007 | writeToClient(s, (uint8_t *)unimplemented, sizeof(unimplemented) - 1); 1008 | exit(RETVAL_UNKNOWN_METHOD); 1009 | } 1010 | 1011 | urlfrom = rindex(url, '/'); 1012 | if (urlfrom == NULL || (conf_hostname && strcasecmp(conf_hostname, hostname) != 0)) 1013 | { 1014 | if (numfields == 3) 1015 | headers(s, STATUS_400, CONTENT_HTML); 1016 | writeToClient(s, (uint8_t *)badrequest, sizeof(badrequest) - 1); 1017 | exit(RETVAL_BAD_REQUEST); 1018 | } 1019 | 1020 | for (servi = services; servi; servi = servi->next) 1021 | { 1022 | if (strcmp(urlfrom + 1, servi->url) == 0) 1023 | break; 1024 | } 1025 | 1026 | if (servi == NULL && conf_udpxy) 1027 | servi = udpxy_parse(url); 1028 | 1029 | free(url); 1030 | url = NULL; 1031 | 1032 | if (servi == NULL) 1033 | { 1034 | if (numfields == 3) 1035 | headers(s, STATUS_404, CONTENT_HTML); 1036 | writeToClient(s, (uint8_t *)serviceNotFound, sizeof(serviceNotFound) - 1); 1037 | exit(RETVAL_CLEAN); 1038 | } 1039 | 1040 | if (clientcount > conf_maxclients) 1041 | { /*Too much clients*/ 1042 | if (numfields == 3) 1043 | headers(s, STATUS_503, CONTENT_HTML); 1044 | writeToClient(s, (uint8_t *)serviceUnavailable, sizeof(serviceUnavailable) - 1); 1045 | exit(RETVAL_CLEAN); 1046 | } 1047 | 1048 | if (strcmp(method, "HEAD") == 0) 1049 | { 1050 | if (numfields == 3) 1051 | headers(s, STATUS_200, CONTENT_OSTREAM); 1052 | exit(RETVAL_CLEAN); 1053 | } 1054 | free(method); 1055 | method = NULL; 1056 | 1057 | if (numfields == 3) 1058 | headers(s, STATUS_200, CONTENT_OSTREAM); 1059 | startRTPstream(s, servi); 1060 | /* SHOULD NEVER REACH HERE */ 1061 | exit(RETVAL_CLEAN); 1062 | } 1063 | -------------------------------------------------------------------------------- /src/rtp2httpd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * RTP2HTTP Proxy - Multicast RTP stream to UNICAST HTTP translator 3 | * 4 | * Copyright (C) 2008-2010 Ondrej Caletka 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License version 2 8 | * as published by the Free Software Foundation. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program (see the file COPYING included with this 17 | * distribution); if not, write to the Free Software Foundation, Inc., 18 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #define _GNU_SOURCE 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "rtp2httpd.h" 38 | 39 | #ifdef HAVE_CONFIG_H 40 | #include "config.h" 41 | #endif /* HAVE_CONFIG_H */ 42 | 43 | #define MAX_S 10 44 | 45 | /** 46 | * Linked list of clients 47 | */ 48 | struct client_s 49 | { 50 | struct sockaddr_storage ss; /* Client host-port */ 51 | pid_t pid; 52 | struct client_s *next; 53 | }; 54 | 55 | static struct client_s *clients; 56 | 57 | /* GLOBALS */ 58 | struct bindaddr_s *bindaddr = NULL; 59 | 60 | int clientcount = 0; 61 | 62 | /* *** */ 63 | 64 | /** 65 | * Logger function. Show the message if current verbosity is above 66 | * logged level. 67 | * 68 | * @param levem Message log level 69 | * @param format printf style format string 70 | * @returns Whatever printf returns 71 | */ 72 | int logger(enum loglevel level, const char *format, ...) 73 | { 74 | va_list ap; 75 | int r = 0; 76 | if (conf_verbosity >= level) 77 | { 78 | va_start(ap, format); 79 | r = vfprintf(stderr, format, ap); 80 | va_end(ap); 81 | } 82 | return r; 83 | } 84 | 85 | void childhandler(int signum) 86 | { /* SIGCHLD handler */ 87 | int child; 88 | int status; 89 | struct client_s *cli, *cli2; 90 | int r; 91 | char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; 92 | 93 | while ((child = waitpid(-1, &status, WNOHANG)) > 0) 94 | { 95 | 96 | for (cli = clients; cli; cli = cli->next) 97 | { 98 | if (child == cli->pid) 99 | break; 100 | } 101 | if (cli != NULL) 102 | { 103 | r = getnameinfo((struct sockaddr *)&(cli->ss), sizeof(cli->ss), 104 | hbuf, sizeof(hbuf), 105 | sbuf, sizeof(sbuf), 106 | NI_NUMERICHOST | NI_NUMERICSERV); 107 | if (r) 108 | { 109 | logger(LOG_ERROR, "getnameinfo failed: %s\n", 110 | gai_strerror(r)); 111 | } 112 | else 113 | { 114 | logger(LOG_DEBUG, "Client %s port %s disconnected (%d, %d)\n", 115 | hbuf, sbuf, WEXITSTATUS(status), 116 | WIFSIGNALED(status)); 117 | } 118 | 119 | /* remove client from the list */ 120 | if (cli == clients) 121 | { 122 | clients = cli->next; 123 | free(cli); 124 | } 125 | else 126 | { 127 | for (cli2 = clients; cli2 != NULL; cli2 = cli2->next) 128 | { 129 | if (cli2->next == cli) 130 | { 131 | cli2->next = cli->next; 132 | free(cli); 133 | break; 134 | } 135 | } 136 | } 137 | } 138 | else 139 | { 140 | if (child != 1) 141 | logger(LOG_ERROR, "Unknown child finished - pid %d\n", child); 142 | } 143 | 144 | clientcount--; 145 | signal(signum, &childhandler); 146 | } 147 | } 148 | 149 | int main(int argc, char *argv[]) 150 | { 151 | struct addrinfo hints, *res, *ai; 152 | struct bindaddr_s *bai; 153 | struct sockaddr_storage client; 154 | socklen_t client_len = sizeof(client); 155 | int cls; 156 | int r, i, j; 157 | int s[MAX_S]; 158 | int maxs, nfds; 159 | char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; 160 | fd_set rfd, rfd0; 161 | pid_t child; 162 | struct client_s *newc; 163 | const int on = 1; 164 | sigset_t childset; 165 | 166 | sigaddset(&childset, SIGCHLD); 167 | 168 | parseCmdLine(argc, argv); 169 | 170 | memset(&hints, 0, sizeof(hints)); 171 | hints.ai_socktype = SOCK_STREAM; 172 | hints.ai_flags = AI_PASSIVE; 173 | maxs = 0; 174 | nfds = -1; 175 | 176 | if (bindaddr == NULL) 177 | { 178 | bindaddr = newEmptyBindaddr(); 179 | } 180 | 181 | for (bai = bindaddr; bai; bai = bai->next) 182 | { 183 | r = getaddrinfo(bai->node, bai->service, 184 | &hints, &res); 185 | if (r) 186 | { 187 | logger(LOG_FATAL, "GAI: %s\n", gai_strerror(r)); 188 | exit(EXIT_FAILURE); 189 | } 190 | 191 | for (ai = res; ai && maxs < MAX_S; ai = ai->ai_next) 192 | { 193 | s[maxs] = socket(ai->ai_family, ai->ai_socktype, 194 | ai->ai_protocol); 195 | if (s[maxs] < 0) 196 | continue; 197 | r = setsockopt(s[maxs], SOL_SOCKET, 198 | SO_REUSEADDR, &on, sizeof(on)); 199 | if (r) 200 | { 201 | logger(LOG_ERROR, "SO_REUSEADDR " 202 | "failed: %s\n", 203 | strerror(errno)); 204 | } 205 | 206 | #ifdef IPV6_V6ONLY 207 | if (ai->ai_family == AF_INET6) 208 | { 209 | r = setsockopt(s[maxs], IPPROTO_IPV6, 210 | IPV6_V6ONLY, &on, sizeof(on)); 211 | if (r) 212 | { 213 | logger(LOG_ERROR, "IPV6_V6ONLY " 214 | "failed: %s\n", 215 | strerror(errno)); 216 | } 217 | } 218 | #endif /* IPV6_V6ONLY */ 219 | 220 | r = bind(s[maxs], ai->ai_addr, ai->ai_addrlen); 221 | if (r) 222 | { 223 | logger(LOG_ERROR, "Cannot bind: %s\n", 224 | strerror(errno)); 225 | close(s[maxs]); 226 | continue; 227 | } 228 | r = listen(s[maxs], 0); 229 | if (r) 230 | { 231 | logger(LOG_ERROR, "Cannot listen: %s\n", 232 | strerror(errno)); 233 | close(s[maxs]); 234 | continue; 235 | } 236 | r = getnameinfo(ai->ai_addr, ai->ai_addrlen, 237 | hbuf, sizeof(hbuf), 238 | sbuf, sizeof(sbuf), 239 | NI_NUMERICHOST | NI_NUMERICSERV); 240 | if (r) 241 | { 242 | logger(LOG_ERROR, "getnameinfo failed: %s\n", 243 | gai_strerror(r)); 244 | } 245 | else 246 | { 247 | logger(LOG_INFO, "Listening on %s port %s\n", 248 | hbuf, sbuf); 249 | } 250 | 251 | if (s[maxs] > nfds) 252 | nfds = s[maxs]; 253 | maxs++; 254 | } 255 | freeaddrinfo(res); 256 | } 257 | freeBindaddr(bindaddr); 258 | 259 | if (maxs == 0) 260 | { 261 | logger(LOG_FATAL, "No socket to listen!\n"); 262 | exit(EXIT_FAILURE); 263 | } 264 | 265 | FD_ZERO(&rfd0); 266 | for (i = 0; i < maxs; i++) 267 | { 268 | FD_SET(s[i], &rfd0); 269 | } 270 | 271 | if (conf_daemonise) 272 | { 273 | logger(LOG_INFO, "Forking to background...\n"); 274 | if (daemon(1, 0) != 0) 275 | { 276 | logger(LOG_FATAL, "Cannot fork: %s\n", strerror(errno)); 277 | exit(EXIT_FAILURE); 278 | } 279 | } 280 | 281 | signal(SIGCHLD, &childhandler); 282 | while (1) 283 | { 284 | rfd = rfd0; 285 | r = select(nfds + 1, &rfd, NULL, NULL, NULL); 286 | if (r < 0) 287 | { 288 | if (errno == EINTR) 289 | continue; 290 | logger(LOG_FATAL, "select() failed: %s\n", 291 | strerror(errno)); 292 | exit(EXIT_FAILURE); 293 | } 294 | for (i = 0; i < maxs; i++) 295 | { 296 | if (FD_ISSET(s[i], &rfd)) 297 | { 298 | cls = accept(s[i], 299 | (struct sockaddr *)&client, 300 | &client_len); 301 | 302 | /* We have to mask SIGCHLD before we add child to the list*/ 303 | sigprocmask(SIG_BLOCK, &childset, NULL); 304 | clientcount++; 305 | if ((child = fork())) 306 | { /* PARENT */ 307 | close(cls); 308 | newc = malloc(sizeof(struct client_s)); 309 | newc->ss = client; 310 | newc->pid = child; 311 | newc->next = clients; 312 | clients = newc; 313 | 314 | r = getnameinfo((struct sockaddr *)&client, client_len, 315 | hbuf, sizeof(hbuf), 316 | sbuf, sizeof(sbuf), 317 | NI_NUMERICHOST | NI_NUMERICSERV); 318 | if (r) 319 | { 320 | logger(LOG_ERROR, "getnameinfo failed: %s\n", 321 | gai_strerror(r)); 322 | } 323 | else 324 | { 325 | logger(LOG_INFO, "Connection from %s port %s\n", 326 | hbuf, sbuf); 327 | } 328 | sigprocmask(SIG_UNBLOCK, &childset, NULL); 329 | } 330 | else 331 | { /* CHILD */ 332 | sigprocmask(SIG_UNBLOCK, &childset, NULL); 333 | for (j = 0; j < maxs; j++) 334 | close(s[j]); 335 | clientService(cls); 336 | exit(EXIT_SUCCESS); 337 | } 338 | } 339 | } 340 | } 341 | /* Should never reach this */ 342 | return 0; 343 | } 344 | -------------------------------------------------------------------------------- /src/rtp2httpd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * RTP2HTTP Proxy - Multicast RTP stream to UNICAST HTTP translator 3 | * 4 | * Copyright (C) 2008,2009 Ondrej Caletka 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License version 2 8 | * as published by the Free Software Foundation. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program (see the file COPYING included with this 17 | * distribution); if not, write to the Free Software Foundation, Inc., 18 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #ifndef __RTP2HTTPD_H__ 22 | #define __RTP2HTTPD_H__ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #ifdef HAVE_CONFIG_H 29 | #include "config.h" 30 | #endif /* HAVE_CONFIG_H */ 31 | 32 | #ifndef SYSCONFDIR 33 | #define SYSCONFDIR "." 34 | #endif /* SYSCONFDIR */ 35 | 36 | #define CONFIGFILE SYSCONFDIR "/rtp2httpd.conf" 37 | 38 | #define max(a, b) ((a) > (b) ? (a) : (b)) 39 | #define min(a, b) ((a) < (b) ? (a) : (b)) 40 | 41 | enum loglevel 42 | { 43 | LOG_FATAL = 0, /* Always shown */ 44 | LOG_ERROR, /* Could be silenced */ 45 | LOG_INFO, /* Default verbosity */ 46 | LOG_DEBUG 47 | }; 48 | 49 | enum fcc_nat_traversal 50 | { 51 | FCC_NAT_T_DISABLED = 0, 52 | FCC_NAT_T_PUNCHHOLE, 53 | FCC_NAT_T_NAT_PMP 54 | }; 55 | 56 | enum service_type 57 | { 58 | SERVICE_MRTP = 0, 59 | SERVICE_MUDP 60 | }; 61 | 62 | /* 63 | * Linked list of adresses to bind 64 | */ 65 | struct bindaddr_s 66 | { 67 | char *node; 68 | char *service; 69 | struct bindaddr_s *next; 70 | }; 71 | 72 | /* 73 | * Linked list of allowed services 74 | */ 75 | struct services_s 76 | { 77 | char *url; 78 | char *msrc; 79 | enum service_type service_type; 80 | struct addrinfo *addr; 81 | struct addrinfo *msrc_addr; 82 | struct addrinfo *fcc_addr; 83 | struct services_s *next; 84 | }; 85 | 86 | /* GLOBAL CONFIGURATION VARIABLES */ 87 | 88 | extern enum loglevel conf_verbosity; 89 | extern int conf_daemonise; 90 | extern int conf_udpxy; 91 | extern int conf_maxclients; 92 | extern char *conf_hostname; 93 | extern enum fcc_nat_traversal conf_fcc_nat_traversal; 94 | extern struct ifreq conf_upstream_interface; 95 | 96 | /* GLOBALS */ 97 | extern struct services_s *services; 98 | extern struct bindaddr_s *bindaddr; 99 | extern int clientcount; 100 | 101 | /* rtp2httpd.c INTERFACE */ 102 | 103 | /** 104 | * Logger function. Show the message if current verbosity is above 105 | * logged level. 106 | * 107 | * @param levem Message log level 108 | * @param format printf style format string 109 | * @returns Whatever printf returns 110 | */ 111 | int logger(enum loglevel level, const char *format, ...); 112 | 113 | /* httpclients.c INTERFACE */ 114 | 115 | /* 116 | * Service for connected client. 117 | * Run in forked thread. 118 | * 119 | * @params s connected socket 120 | */ 121 | void clientService(int s); 122 | 123 | /* Return values of clientService() */ 124 | #define RETVAL_CLEAN 0 125 | #define RETVAL_WRITE_FAILED 1 126 | #define RETVAL_READ_FAILED 2 127 | #define RETVAL_UNKNOWN_METHOD 3 128 | #define RETVAL_BAD_REQUEST 4 129 | #define RETVAL_RTP_FAILED 5 130 | #define RETVAL_SOCK_READ_FAILED 6 131 | 132 | /* configfile.c INTERFACE */ 133 | 134 | void parseCmdLine(int argc, char *argv[]); 135 | struct bindaddr_s *newEmptyBindaddr(); 136 | void freeBindaddr(struct bindaddr_s *); 137 | 138 | #endif /* __RTP2HTTPD_H__*/ 139 | 140 | #ifndef strndupa 141 | #define strndupa(s, n) \ 142 | (__extension__({const char *__in = (s); \ 143 | size_t __len = strnlen (__in, (n)) + 1; \ 144 | char *__out = (char *) alloca (__len); \ 145 | __out[__len-1] = '\0'; \ 146 | (char *) memcpy (__out, __in, __len-1); })) 147 | #endif 148 | --------------------------------------------------------------------------------