├── .gitignore ├── AUTHORS ├── DAI.md ├── DRM.md ├── LICENSE ├── README.md ├── TODO.md ├── config ├── dash ├── ngx_rtmp_cenc.c ├── ngx_rtmp_cenc.h ├── ngx_rtmp_dash_module.c ├── ngx_rtmp_dash_templates.h ├── ngx_rtmp_mp4.c └── ngx_rtmp_mp4.h ├── doc ├── README.md ├── control_modul.md ├── debug_log.md ├── directives.md ├── examples.md ├── exec_wrapper_in_bash.md ├── faq.md ├── getting_number_of_subscribers.md ├── getting_started.md ├── installing_in_gentoo.md ├── installing_ubuntu_using_ppas.md └── tutorial.md ├── hls ├── ngx_rtmp_hls_module.c ├── ngx_rtmp_mpegts.c ├── ngx_rtmp_mpegts.h ├── ngx_rtmp_mpegts_crc.c └── ngx_rtmp_mpegts_crc.h ├── ngx_rtmp.c ├── ngx_rtmp.h ├── ngx_rtmp_access_module.c ├── ngx_rtmp_amf.c ├── ngx_rtmp_amf.h ├── ngx_rtmp_auto_push_module.c ├── ngx_rtmp_bandwidth.c ├── ngx_rtmp_bandwidth.h ├── ngx_rtmp_bitop.c ├── ngx_rtmp_bitop.h ├── ngx_rtmp_cmd_module.c ├── ngx_rtmp_cmd_module.h ├── ngx_rtmp_codec_module.c ├── ngx_rtmp_codec_module.h ├── ngx_rtmp_control_module.c ├── ngx_rtmp_core_module.c ├── ngx_rtmp_eval.c ├── ngx_rtmp_eval.h ├── ngx_rtmp_exec_module.c ├── ngx_rtmp_flv_module.c ├── ngx_rtmp_handler.c ├── ngx_rtmp_handshake.c ├── ngx_rtmp_init.c ├── ngx_rtmp_limit_module.c ├── ngx_rtmp_live_module.c ├── ngx_rtmp_live_module.h ├── ngx_rtmp_log_module.c ├── ngx_rtmp_mp4_module.c ├── ngx_rtmp_netcall_module.c ├── ngx_rtmp_netcall_module.h ├── ngx_rtmp_notify_module.c ├── ngx_rtmp_play_module.c ├── ngx_rtmp_play_module.h ├── ngx_rtmp_proxy_protocol.c ├── ngx_rtmp_proxy_protocol.h ├── ngx_rtmp_receive.c ├── ngx_rtmp_record_module.c ├── ngx_rtmp_record_module.h ├── ngx_rtmp_relay_module.c ├── ngx_rtmp_relay_module.h ├── ngx_rtmp_send.c ├── ngx_rtmp_shared.c ├── ngx_rtmp_stat_module.c ├── ngx_rtmp_streams.h ├── ngx_rtmp_version.h ├── stat.xsl └── test ├── README.md ├── dump.sh ├── ffstream.sh ├── nginx.conf ├── play.sh ├── rtmp-publisher ├── README.md ├── RtmpPlayer.mxml ├── RtmpPlayer.swf ├── RtmpPlayerLight.mxml ├── RtmpPlayerLight.swf ├── RtmpPublisher.mxml ├── RtmpPublisher.swf ├── player.html ├── publisher.html └── swfobject.js └── www ├── bg.jpg ├── index.html ├── jwplayer ├── jwplayer.flash.swf └── jwplayer.js ├── jwplayer_old ├── player.swf └── swfobject.js └── record.html /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /.settings 3 | /.project 4 | /.cproject 5 | /.vscode 6 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Project author: 2 | 3 | Roman Arutyunyan 4 | Moscow, Russia 5 | 6 | Contacts: 7 | arut@qip.ru 8 | arutyunyan.roman@gmail.com 9 | 10 | Fork author: 11 | Sergey Dryanzhinsky 12 | Moscow, Russia 13 | 14 | Contacts: 15 | sergey.dryabzhinsky@gmail.com 16 | 17 | Fork ot the fork author: 18 | Raphael Mazelier 19 | Paris, France 20 | 21 | Contacts: 22 | raph@futomaki.net 23 | -------------------------------------------------------------------------------- /DAI.md: -------------------------------------------------------------------------------- 1 | 2 | ## AD Insertion markers 3 | 4 | nginx-rtmp module can interpret ad insertion markers message sent by encoder in amf rmtp message. 5 | It then extract the information and write it in an emsg box at the begining of the next chunk. 6 | The emsg box contains the relative start time of the ad-break, its duration, and possibly some metadata. 7 | Enabling ad_marker also add the following inband event in the manifest. 8 | This is the responsability of the player to watch this event, and to treat it. 9 | 10 | ``` 11 | 12 | ``` 13 | 14 | Options to enable ad marker processing are : 15 | 16 | ``` 17 | dash_ad_markers off|on_cuepoint|on_cuepoint_scte35; 18 | ``` 19 | 20 | - on_cuepoint is the simple variant (without scte message) 21 | - on_cuepoint_scte35 is the scte35 variant with program_id metadata 22 | 23 | ``` 24 | dash_ad_markers_timehack off|on; 25 | ``` 26 | 27 | - off implement the standard timing as described in reference documentation 28 | - on implement a hack on the start time. This is need to be more resilient. Warning this need a patched version of your player. (currently this is what is test and in production with a one line patch on dashjs) 29 | 30 | Currently there is only elemental encoder tested and compliant. 31 | 32 | See [here](https://theyosh.nl/speeltuin/dash/dash.js-2.0.0/samples/ad-insertion/) for the original examples and inspiration. 33 | -------------------------------------------------------------------------------- /DRM.md: -------------------------------------------------------------------------------- 1 | 2 | ## DRM Common Encryption 3 | 4 | This fork give the possibility of packaging dash "protected" stream. 5 | Concretely it implement the minimal requirement of "common-encryption" as described in ISO/IEC 23001-7:2015, Information technology — MPEG systems technologies — Part 7: Common encryption in ISO Base Media File Format files - 2nd Edition. 6 | You can read a brief description here : "https://w3c.github.io/encrypted-media/format-registry/stream/mp4.html#bib-CENC" 7 | 8 | ### How to use it : 9 | 10 | You need at least to enable common_encryption and provide one key and one key id with the following directives : 11 | 12 | ``` 13 | dash_cenc on; # enable common encryption on all stream in this block 14 | dash_cenc_kid XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; # 16 bytes KEY-ID in hex 15 | dash_cenc_key XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; # 16 bytes KEY in hex 16 | ``` 17 | 18 | It enable automatically Clear-Key pseudo DRM. (use it only for testing purpose) 19 | 20 | Currently the are two real DRM supported : Widevine and Microsoft Playready. 21 | 22 | For widevine you need the following directives in addition : 23 | 24 | ``` 25 | dash_wdv on; # enable widevine signalling 26 | dash_wdv_data AAAAbHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAA... ; # base64 encoded widevine pssh 27 | ``` 28 | 29 | For playready you need the following directives in addition (you can use both widevine and playready together with the same kid:key pair): 30 | 31 | ``` 32 | dash_mspr on; # enable playready signalling 33 | dash_mspr_data AAACsHBzc2gAAAAAmgTweZhAQoarkuZb4Ih...; # base64 encoded playready pssh 34 | dash_mspr_kid AAATH/7xxxfUbpB8mhqA==; # base64 encoded playready kid 35 | dash_mspr_pro kAIAAAEAAQCGAjwAVwBSAE0ASABFAEEARA...; # base64 encoded playready PRO (Playready Object) 36 | ``` 37 | 38 | ### Implementation : 39 | 40 | _TLDR;_ This was quite an adventure 41 | 42 | The implementation is based on the ISO_IEC_23001-7_2016 normative document. 43 | I also took lot of inspiration on kaltura nginx-vod module. 44 | 45 | It implement the minimal requirement of the norm, the 'cenc' scheme, AES-CTR mode full sample and video NAL Subsample encryption. 46 | 47 | Audio track are encrypted in full sample mode with AES-CTR. 48 | 49 | Video track are encrypted in sub sample mode, assuming one NALU per frame, using enough clear text size at the beginning of the frame to keep the NAL header in clear. (the module does not analyse NAL Headers). 50 | 51 | The clear size is rounded to make encrypted size of data a multiple of the AES-CRT block size. 52 | 53 | The implementation allow only one KID:KEY couple used for all tracks. 54 | 55 | The implementation use 64bits IVs. 56 | 57 | ### Conformity : 58 | 59 | This implementation have been tested with and known working : 60 | 61 | Clearkey : 62 | - Firefox: dashjs/shakaplayer 63 | - Chrome: dashjs/shakaplayer 64 | 65 | Widevine : 66 | - Firefox: dashjs/shakaplayer 67 | - Chrome: dashjs/shakaplayer 68 | 69 | Playready: 70 | - Edge : dashjs/shakaplayer 71 | 72 | Bitmovin player seem also to work. 73 | 74 | ### Thanks: 75 | 76 | - Thanks to all the opensource communauty. 77 | - Special thanks to Eran Kornblau from Kaltura, Joey Parrish, and Jacob Timble. 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2014, Roman Arutyunyan 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NGINX-based Media Streaming Server 2 | 3 | ## nginx-rtmp-module (dash enhanced version) 4 | 5 | Forked from https://github.com/sergey-dryabzhinsky/ which was the most up to date version (until now) 6 | 7 | Notable new features : 8 | 9 | - add the possibility to make adaptative streaming (show below configuration, using ffmpeg to trancode in 3 variants, and produce one manifest). 10 | note the "max" flag which indicate which representation should have max witdh and height and so use it to create the variant manifest. 11 | you can also use any encoder to directly push the variant. 12 | - add the support of using repetition in manifest to shorten them (option dash_repetition) (thanks to Streamroot) 13 | - add the support of common-encryption; currently working DRM are ClearKey/Widevine/Playready (see specific doc [here](DRM.md)) 14 | - add the support of ad insertion break event, from rtmp AMF message to dash (InbandEvent in manifest and emsg box in mp4 fragment, see doc [here](DAI.md)) 15 | 16 | 17 | See original doc here for full list of options. 18 | 19 | ``` 20 | rtmp { 21 | server { 22 | listen 1935; 23 | 24 | application ingest { 25 | live on; 26 | exec /usr/bin/ffmpeg -i rtmp://localhost/$app/$name \ 27 | -c:a libfdk_aac -b:a 64k -c:v libx264 -preset fast -profile:v baseline -vsync cfr -s 1024x576 -b:v 1024K -bufsize 1024k \ 28 | -f flv rtmp://localhost/dash/$name_hi \ 29 | -c:a libfdk_aac -b:a 64k -c:v libx264 -preset fast -profile:v baseline -vsync cfr -s 640x360 -b:v 832K -bufsize 832k \ 30 | -f flv rtmp://localhost/dash/$name_med \ 31 | -c:a libfdk_aac -b:a 64k -c:v libx264 -preset fast -profile:v baseline -vsync cfr -s 320x180 -b:v 256K -bufsize 256k \ 32 | -f flv rtmp://localhost/dash/$name_low 33 | } 34 | 35 | application dash { 36 | live on; 37 | dash on; 38 | dash_nested on; 39 | dash_repetition on; 40 | dash_path /dev/shm/dash; 41 | dash_fragment 4; # 4 second is generaly a good choice for live 42 | dash_playlist_length 120; # keep 120s of tail 43 | dash_cleanup on; 44 | dash_variant _low bandwidth="256000" width="320" height="180"; 45 | dash_variant _med bandwidth="832000" width="640" height="360"; 46 | dash_variant _hi bandwidth="1024000" width="1024" height="576" max; 47 | } 48 | } 49 | 50 | server { 51 | listen 443 ssl; 52 | location / { 53 | root /var/www; 54 | add_header Cache-Control no-cache; 55 | add_header 'Access-Control-Allow-Origin' '*'; 56 | } 57 | location /dash/live/index.mpd { 58 | alias /dev/shm/dash/live/index.mpd; 59 | add_header 'Access-Control-Allow-Origin' '*'; 60 | add_header Cache-Control 'public, max-age=0, s-maxage=2'; 61 | } 62 | location /dash/live { 63 | alias /dev/shm/dash/live; 64 | add_header 'Access-Control-Allow-Origin' '*'; 65 | add_header Cache-Control 'public, max-age=600, s-maxage=600'; 66 | } 67 | 68 | server_name live.site.net; 69 | ssl_certificate /etc/letsencrypt/live/live.site.net/fullchain.pem; 70 | ssl_certificate_key /etc/letsencrypt/live/live.sit.net/privkey.pem; 71 | 72 | } 73 | } 74 | ``` 75 | 76 | 77 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | ## TODO 2 | 3 | - doc // OK 4 | - need re-upstream // wait response 5 | - rewritte the variant code for dash, using memory ? 6 | 7 | - add option emsg_timestamp_hack 8 | 9 | 10 | - test common encryption code (OK clearkey, Widevine both on chrome/firefox!) 11 | - need to write some doc about cenc/drm implem 12 | - refacto code for writing content protection in manifest // OK 13 | - clarify the use of %V in ngx_printf // OK 14 | - refacto code for init / kid // OK 15 | - correct pssh in manifest // OK 16 | - add pssh / cenc in variant mpd // OK 17 | - add real base64 encrypt pssh data // OK 18 | - add support for sub sample encryption //OK 19 | - add sig for wdv //OK 20 | - add struct for drm info // OK 21 | - add pssh in init file for wdv // OK 22 | - add msplayready support // OK need pssh in init file 23 | 24 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | ngx_addon_name="ngx_rtmp_module" 2 | RTMP_CORE_MODULES=" \ 3 | ngx_rtmp_module \ 4 | ngx_rtmp_core_module \ 5 | ngx_rtmp_cmd_module \ 6 | ngx_rtmp_codec_module \ 7 | ngx_rtmp_access_module \ 8 | ngx_rtmp_record_module \ 9 | ngx_rtmp_live_module \ 10 | ngx_rtmp_play_module \ 11 | ngx_rtmp_flv_module \ 12 | ngx_rtmp_mp4_module \ 13 | ngx_rtmp_netcall_module \ 14 | ngx_rtmp_relay_module \ 15 | ngx_rtmp_exec_module \ 16 | ngx_rtmp_auto_push_module \ 17 | ngx_rtmp_auto_push_index_module \ 18 | ngx_rtmp_notify_module \ 19 | ngx_rtmp_log_module \ 20 | ngx_rtmp_limit_module \ 21 | ngx_rtmp_hls_module \ 22 | ngx_rtmp_dash_module \ 23 | " 24 | RTMP_HTTP_MODULES=" \ 25 | ngx_rtmp_stat_module \ 26 | ngx_rtmp_control_module \ 27 | " 28 | RTMP_DEPS=" \ 29 | $ngx_addon_dir/ngx_rtmp_amf.h \ 30 | $ngx_addon_dir/ngx_rtmp_bandwidth.h \ 31 | $ngx_addon_dir/ngx_rtmp_cmd_module.h \ 32 | $ngx_addon_dir/ngx_rtmp_codec_module.h \ 33 | $ngx_addon_dir/ngx_rtmp_eval.h \ 34 | $ngx_addon_dir/ngx_rtmp.h \ 35 | $ngx_addon_dir/ngx_rtmp_version.h \ 36 | $ngx_addon_dir/ngx_rtmp_live_module.h \ 37 | $ngx_addon_dir/ngx_rtmp_netcall_module.h \ 38 | $ngx_addon_dir/ngx_rtmp_play_module.h \ 39 | $ngx_addon_dir/ngx_rtmp_record_module.h \ 40 | $ngx_addon_dir/ngx_rtmp_relay_module.h \ 41 | $ngx_addon_dir/ngx_rtmp_streams.h \ 42 | $ngx_addon_dir/ngx_rtmp_bitop.h \ 43 | $ngx_addon_dir/ngx_rtmp_proxy_protocol.h \ 44 | $ngx_addon_dir/hls/ngx_rtmp_mpegts.h \ 45 | $ngx_addon_dir/dash/ngx_rtmp_mp4.h \ 46 | $ngx_addon_dir/dash/ngx_rtmp_cenc.h \ 47 | $ngx_addon_dir/dash/ngx_rtmp_dash_templates.h \ 48 | " 49 | RTMP_CORE_SRCS=" \ 50 | $ngx_addon_dir/ngx_rtmp.c \ 51 | $ngx_addon_dir/ngx_rtmp_init.c \ 52 | $ngx_addon_dir/ngx_rtmp_handshake.c \ 53 | $ngx_addon_dir/ngx_rtmp_handler.c \ 54 | $ngx_addon_dir/ngx_rtmp_amf.c \ 55 | $ngx_addon_dir/ngx_rtmp_send.c \ 56 | $ngx_addon_dir/ngx_rtmp_shared.c \ 57 | $ngx_addon_dir/ngx_rtmp_eval.c \ 58 | $ngx_addon_dir/ngx_rtmp_receive.c \ 59 | $ngx_addon_dir/ngx_rtmp_core_module.c \ 60 | $ngx_addon_dir/ngx_rtmp_cmd_module.c \ 61 | $ngx_addon_dir/ngx_rtmp_codec_module.c \ 62 | $ngx_addon_dir/ngx_rtmp_access_module.c \ 63 | $ngx_addon_dir/ngx_rtmp_record_module.c \ 64 | $ngx_addon_dir/ngx_rtmp_live_module.c \ 65 | $ngx_addon_dir/ngx_rtmp_play_module.c \ 66 | $ngx_addon_dir/ngx_rtmp_flv_module.c \ 67 | $ngx_addon_dir/ngx_rtmp_mp4_module.c \ 68 | $ngx_addon_dir/ngx_rtmp_netcall_module.c \ 69 | $ngx_addon_dir/ngx_rtmp_relay_module.c \ 70 | $ngx_addon_dir/ngx_rtmp_bandwidth.c \ 71 | $ngx_addon_dir/ngx_rtmp_exec_module.c \ 72 | $ngx_addon_dir/ngx_rtmp_auto_push_module.c \ 73 | $ngx_addon_dir/ngx_rtmp_notify_module.c \ 74 | $ngx_addon_dir/ngx_rtmp_log_module.c \ 75 | $ngx_addon_dir/ngx_rtmp_limit_module.c \ 76 | $ngx_addon_dir/ngx_rtmp_bitop.c \ 77 | $ngx_addon_dir/ngx_rtmp_proxy_protocol.c \ 78 | $ngx_addon_dir/hls/ngx_rtmp_hls_module.c \ 79 | $ngx_addon_dir/dash/ngx_rtmp_dash_module.c \ 80 | $ngx_addon_dir/hls/ngx_rtmp_mpegts.c \ 81 | $ngx_addon_dir/hls/ngx_rtmp_mpegts_crc.c \ 82 | $ngx_addon_dir/dash/ngx_rtmp_mp4.c \ 83 | $ngx_addon_dir/dash/ngx_rtmp_cenc.c \ 84 | " 85 | RTMP_HTTP_SRCS=" \ 86 | $ngx_addon_dir/ngx_rtmp_stat_module.c \ 87 | $ngx_addon_dir/ngx_rtmp_control_module.c \ 88 | " 89 | ngx_module_incs=$ngx_addon_dir 90 | ngx_module_deps=$RTMP_DEPS 91 | 92 | if [ "$ngx_module_link" = "" ] ; then 93 | # Old nginx version 94 | ngx_module_link=NONE 95 | 96 | EVENT_MODULES="$EVENT_MODULES $RTMP_CORE_MODULES" 97 | HTTP_MODULES="$HTTP_MODULES $RTMP_HTTP_MODULES" 98 | NGX_ADDON_DEPS="$NGX_ADDON_DEPS $RTMP_DEPS" 99 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $RTMP_CORE_SRCS $RTMP_HTTP_SRCS" 100 | fi 101 | 102 | if [ $ngx_module_link = DYNAMIC ] ; then 103 | ngx_module_name="$RTMP_CORE_MODULES $RTMP_HTTP_MODULES" 104 | ngx_module_srcs="$RTMP_CORE_SRCS $RTMP_HTTP_SRCS" 105 | . auto/module 106 | elif [ $ngx_module_link = ADDON ] ; then 107 | ngx_module_type=EVENT 108 | ngx_module_name=$RTMP_CORE_MODULES 109 | ngx_module_srcs=$RTMP_CORE_SRCS 110 | . auto/module 111 | ngx_module_type=HTTP 112 | ngx_module_name=$RTMP_HTTP_MODULES 113 | ngx_module_srcs=$RTMP_HTTP_SRCS 114 | . auto/module 115 | fi 116 | 117 | USE_OPENSSL=YES 118 | 119 | CFLAGS="$CFLAGS -I$ngx_addon_dir" 120 | # Debug build with all warnings as errors 121 | # CFLAGS="$CFLAGS -I$ngx_addon_dir -Wall -Wpointer-arith -Wno-unused-parameter -Werror" 122 | -------------------------------------------------------------------------------- /dash/ngx_rtmp_cenc.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "ngx_rtmp_cenc.h" 10 | 11 | 12 | void 13 | debug_counter(ngx_rtmp_session_t *s, uint8_t *c, uint8_t *k, size_t l) 14 | { 15 | u_char hexc[AES_BLOCK_SIZE*2+1]; 16 | u_char hexk[AES_BLOCK_SIZE*2+1]; 17 | 18 | ngx_hex_dump(hexc, c, AES_BLOCK_SIZE); 19 | ngx_hex_dump(hexk, k, AES_BLOCK_SIZE); 20 | hexc[AES_BLOCK_SIZE*2] = '\0'; 21 | hexk[AES_BLOCK_SIZE*2] = '\0'; 22 | 23 | ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, 24 | "dash cenc_counter: %ui %s %s", l, hexc, hexk); 25 | } 26 | 27 | 28 | ngx_int_t 29 | ngx_rtmp_cenc_read_hex(ngx_str_t src, u_char* dst) 30 | { 31 | u_char l, h; 32 | size_t i; 33 | 34 | if (src.len != NGX_RTMP_CENC_KEY_SIZE*2) { 35 | return NGX_ERROR; 36 | } 37 | 38 | for (i = 0; i < NGX_RTMP_CENC_KEY_SIZE; i++) { 39 | l = ngx_tolower(src.data[i*2]); 40 | l = l >= 'a' ? l - 'a' + 10 : l - '0'; 41 | h = ngx_tolower(src.data[i*2+1]); 42 | h = h >= 'a' ? h - 'a' + 10 : h - '0'; 43 | dst[i] = (l << 4) | h; 44 | } 45 | 46 | return NGX_OK; 47 | } 48 | 49 | 50 | ngx_int_t 51 | ngx_rtmp_cenc_rand_iv(u_char* iv) 52 | { 53 | if(RAND_bytes(iv, NGX_RTMP_CENC_IV_SIZE) != 1) { 54 | return NGX_ERROR; 55 | } 56 | 57 | return NGX_OK; 58 | } 59 | 60 | 61 | void 62 | ngx_rtmp_cenc_increment_iv(u_char* iv) 63 | { 64 | int i; 65 | 66 | for (i = NGX_RTMP_CENC_IV_SIZE - 1; i >= 0; i--) { 67 | iv[i]++; 68 | if (iv[i]) 69 | break; 70 | } 71 | } 72 | 73 | 74 | ngx_int_t 75 | ngx_rtmp_cenc_aes_ctr_encrypt(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, 76 | uint8_t *data, size_t data_len) 77 | { 78 | /* aes-ctr implementation */ 79 | 80 | EVP_CIPHER_CTX* ctx; 81 | size_t j, len, left = data_len; 82 | int i, w; 83 | uint8_t *pos = data; 84 | uint8_t counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE]; 85 | 86 | ngx_memset(counter + NGX_RTMP_CENC_IV_SIZE, 0, NGX_RTMP_CENC_IV_SIZE); 87 | ngx_memcpy(counter, iv, NGX_RTMP_CENC_IV_SIZE); 88 | 89 | ctx = EVP_CIPHER_CTX_new(); 90 | if (ctx == NULL) { 91 | ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, 92 | "dash rtmp_cenc_encrypt: evp_cipher_ctx failed"); 93 | return NGX_ERROR; 94 | } 95 | 96 | if (EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, NULL) != 1) { 97 | ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, 98 | "dash rtmp_cenc_encrypt: evp_encrypt_init failed"); 99 | return NGX_ERROR; 100 | } 101 | 102 | while (left > 0) { 103 | 104 | if (EVP_EncryptUpdate(ctx, buf, &w, counter, AES_BLOCK_SIZE) != 1) { 105 | ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, 106 | "dash rtmp_cenc_encrypt: evp_encrypt_update failed"); 107 | return NGX_ERROR; 108 | } 109 | 110 | len = (left < AES_BLOCK_SIZE) ? left : AES_BLOCK_SIZE; 111 | for (j = 0; j < len; j++) 112 | pos[j] ^= buf[j]; 113 | pos += len; 114 | left -= len; 115 | 116 | for (i = AES_BLOCK_SIZE - 1; i >= 0; i--) { 117 | counter[i]++; 118 | if (counter[i]) 119 | break; 120 | } 121 | } 122 | 123 | EVP_CIPHER_CTX_free(ctx); 124 | 125 | return NGX_OK; 126 | } 127 | 128 | 129 | ngx_int_t 130 | ngx_rtmp_cenc_encrypt_full_sample(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, 131 | uint8_t *data, size_t data_len) 132 | { 133 | return ngx_rtmp_cenc_aes_ctr_encrypt(s, key, iv, data, data_len); 134 | } 135 | 136 | 137 | ngx_int_t 138 | ngx_rtmp_cenc_encrypt_sub_sample(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, 139 | uint8_t *data, size_t data_len, size_t *clear_data_len) 140 | { 141 | size_t crypted_data_len; 142 | 143 | /* small sample : leave it in clear */ 144 | if (data_len <= NGX_RTMP_CENC_MIN_CLEAR_SIZE) { 145 | *clear_data_len = data_len; 146 | return NGX_OK; 147 | } 148 | 149 | /* skip sufficient amount of data to leave nalu header/infos 150 | * in clear to conform to the norm */ 151 | crypted_data_len = 152 | ((data_len - NGX_RTMP_CENC_MIN_CLEAR_SIZE) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE; 153 | *clear_data_len = data_len - crypted_data_len; 154 | 155 | data += *clear_data_len; 156 | return ngx_rtmp_cenc_aes_ctr_encrypt(s, key, iv, data, crypted_data_len); 157 | 158 | } 159 | 160 | 161 | ngx_int_t 162 | ngx_rtmp_cenc_content_protection_pssh(u_char* kid, ngx_str_t *dest_pssh) 163 | { 164 | ngx_str_t src_pssh; 165 | u_char dest[NGX_RTMP_CENC_MAX_PSSH_SIZE]; 166 | 167 | u_char pssh[] = { 168 | 0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68, // pssh box header 169 | 0x01, 0x00, 0x00, 0x00, // header 170 | 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // systemID 171 | 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b, 172 | 0x00, 0x00, 0x00, 0x01, // kid count 173 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // kid 174 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 175 | 0x00, 0x00, 0x00, 0x00 // data size 176 | }; 177 | 178 | ngx_memcpy(pssh+32, kid, NGX_RTMP_CENC_KEY_SIZE); 179 | 180 | src_pssh.len = sizeof(pssh); 181 | src_pssh.data = pssh; 182 | 183 | dest_pssh->len = ngx_base64_encoded_length(src_pssh.len); 184 | dest_pssh->data = dest; 185 | 186 | ngx_encode_base64(dest_pssh, &src_pssh); 187 | 188 | return NGX_OK; 189 | } 190 | 191 | -------------------------------------------------------------------------------- /dash/ngx_rtmp_cenc.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_RTMP_CENC_H_INCLUDED_ 2 | #define _NGX_RTMP_CENC_H_INCLUDED_ 3 | 4 | 5 | #define NGX_RTMP_CENC_IV_SIZE (8) 6 | #define NGX_RTMP_CENC_KEY_SIZE (16) 7 | #define NGX_RTMP_CENC_MIN_CLEAR_SIZE (100) 8 | #define NGX_RTMP_CENC_MAX_PSSH_SIZE (1024) 9 | 10 | 11 | typedef struct { 12 | u_char kid[NGX_RTMP_CENC_KEY_SIZE]; 13 | unsigned wdv:1; 14 | ngx_str_t wdv_data; 15 | unsigned mspr:1; 16 | ngx_str_t mspr_data; 17 | ngx_str_t mspr_kid; 18 | ngx_str_t mspr_pro; 19 | } ngx_rtmp_cenc_drm_info_t; 20 | 21 | 22 | ngx_int_t 23 | ngx_rtmp_cenc_read_hex(ngx_str_t src, u_char* dst); 24 | 25 | ngx_int_t 26 | ngx_rtmp_cenc_rand_iv(u_char* iv); 27 | 28 | void 29 | ngx_rtmp_cenc_increment_iv(u_char* iv); 30 | 31 | ngx_int_t 32 | ngx_rtmp_cenc_encrypt_full_sample(ngx_rtmp_session_t *s, 33 | uint8_t *key, uint8_t *iv, uint8_t *data, size_t data_len); 34 | 35 | ngx_int_t 36 | ngx_rtmp_cenc_encrypt_sub_sample(ngx_rtmp_session_t *s, 37 | uint8_t *key, uint8_t *iv, uint8_t *data, 38 | size_t data_len, size_t *clear_data_len); 39 | 40 | ngx_int_t 41 | ngx_rtmp_cenc_content_protection_pssh(u_char* kid, 42 | ngx_str_t *dest_pssh); 43 | 44 | #endif /* _NGX_RTMP_CENC_H_INCLUDED_ */ 45 | -------------------------------------------------------------------------------- /dash/ngx_rtmp_dash_templates.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_RTMP_DASH_TEMPLATES 2 | #define _NGX_RTMP_DASH_TEMPLATES 3 | 4 | 5 | #define NGX_RTMP_DASH_MANIFEST_HEADER \ 6 | "\n" \ 7 | "\n" 22 | 23 | 24 | #define NGX_RTMP_DASH_MANIFEST_PERIOD \ 25 | " \n" 26 | 27 | 28 | #define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO \ 29 | " \n" 37 | 38 | 39 | #define NGX_RTMP_DASH_INBAND_EVENT \ 40 | " \n" 43 | 44 | 45 | #define NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_CENC \ 46 | " \n" 52 | 53 | 54 | #define NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC \ 55 | " \n" \ 58 | " %V\n" \ 59 | " \n" 60 | 61 | 62 | #define NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_WDV \ 63 | " \n" \ 66 | " %V\n" \ 67 | " \n" 68 | 69 | 70 | #define NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_MSPR \ 71 | " \n" \ 75 | " %V\n" \ 76 | " 1\n" \ 77 | " 8\n" \ 78 | " %V\n" \ 79 | " %V\n" \ 80 | " \n" 81 | 82 | 83 | #define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO \ 84 | " \n" \ 93 | " \n" \ 98 | " \n" 99 | 100 | 101 | #define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VARIANT_VIDEO \ 102 | " \n" 115 | 116 | 117 | #define NGX_RTMP_DASH_MANIFEST_SEGMENTTPL_VARIANT_VIDEO \ 118 | " \n" \ 123 | " \n" 124 | 125 | 126 | #define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO_FOOTER \ 127 | " \n" \ 128 | " \n" \ 129 | " \n" 130 | 131 | 132 | #define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO_FOOTER \ 133 | " \n" 134 | 135 | 136 | // " \n" 137 | 138 | #define NGX_RTMP_DASH_MANIFEST_TIME \ 139 | " \n" 140 | 141 | 142 | #define NGX_RTMP_DASH_MANIFEST_TIME_WITH_REPETITION \ 143 | " \n" 144 | 145 | 146 | #define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO \ 147 | " \n" \ 151 | " \n" 155 | 156 | 157 | #define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO \ 158 | " \n" \ 164 | " \n" \ 169 | " \n" 170 | 171 | 172 | #define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO_FOOTER \ 173 | " \n" \ 174 | " \n" \ 175 | " \n" 176 | 177 | 178 | #define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO_FOOTER \ 179 | " \n" 180 | 181 | 182 | #define NGX_RTMP_DASH_PERIOD_FOOTER \ 183 | " \n" 184 | 185 | 186 | #define NGX_RTMP_DASH_MANIFEST_CLOCK \ 187 | " \n" 189 | 190 | 191 | #define NGX_RTMP_DASH_MANIFEST_FOOTER \ 192 | "\n" 193 | 194 | 195 | #endif 196 | -------------------------------------------------------------------------------- /dash/ngx_rtmp_mp4.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef _NGX_RTMP_MP4_H_INCLUDED_ 4 | #define _NGX_RTMP_MP4_H_INCLUDED_ 5 | 6 | 7 | #include 8 | #include 9 | #include 10 | #include "ngx_rtmp_cenc.h" 11 | 12 | 13 | #define NGX_RTMP_MP4_SAMPLE_SIZE 0x01 14 | #define NGX_RTMP_MP4_SAMPLE_DURATION 0x02 15 | #define NGX_RTMP_MP4_SAMPLE_DELAY 0x04 16 | #define NGX_RTMP_MP4_SAMPLE_KEY 0x08 17 | 18 | 19 | typedef struct { 20 | uint32_t size; 21 | uint32_t clear_size; 22 | uint32_t duration; 23 | uint32_t delay; 24 | uint32_t timestamp; 25 | unsigned key:1; 26 | unsigned is_protected:1; 27 | u_char iv[NGX_RTMP_CENC_IV_SIZE]; 28 | } ngx_rtmp_mp4_sample_t; 29 | 30 | 31 | typedef enum { 32 | NGX_RTMP_MP4_FILETYPE_INIT, 33 | NGX_RTMP_MP4_FILETYPE_SEG 34 | } ngx_rtmp_mp4_file_type_t; 35 | 36 | 37 | typedef enum { 38 | NGX_RTMP_MP4_VIDEO_TRACK, 39 | NGX_RTMP_MP4_AUDIO_TRACK, 40 | NGX_RTMP_MP4_EVIDEO_TRACK, 41 | NGX_RTMP_MP4_EAUDIO_TRACK 42 | } ngx_rtmp_mp4_track_type_t; 43 | 44 | 45 | ngx_int_t ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b); 46 | ngx_int_t ngx_rtmp_mp4_write_styp(ngx_buf_t *b); 47 | ngx_int_t ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, 48 | ngx_rtmp_mp4_track_type_t ttype, ngx_rtmp_cenc_drm_info_t *drmi); 49 | ngx_int_t ngx_rtmp_mp4_write_moof(ngx_buf_t *b, uint32_t earliest_pres_time, 50 | char type, uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples, 51 | ngx_uint_t sample_mask, uint32_t index, ngx_flag_t is_protected); 52 | ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_buf_t *b, 53 | ngx_uint_t reference_size, uint32_t earliest_pres_time, 54 | uint32_t latest_pres_time); 55 | ngx_int_t ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size); 56 | ngx_int_t ngx_rtmp_mp4_write_emsg(ngx_buf_t *b, 57 | uint32_t earliest_pres_time, uint32_t cuepoint_time, uint32_t duration_time, uint32_t id); 58 | 59 | #endif /* _NGX_RTMP_MP4_H_INCLUDED_ */ 60 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to the nginx-rtmp-module documentation! 2 | 3 | ## Pages 4 | 5 | * [Control module](control_modul.md) 6 | * [Debug log](debug_log.md) 7 | * [Directives](directives.md) 8 | * [Examples](examples.md) 9 | * [Exec wrapper in bash](exec_wrapper_in_bash.md) 10 | * [FAQ](faq.md) 11 | * [Getting number of subscribers](getting_number_of_subscribers.md) 12 | * [Getting started with nginx rtmp](getting_started.md) 13 | * [Installing in Gentoo](Installing_in_gentoo.md) 14 | * [Installing on Ubuntu using PPAs](installing_ubuntu_using_ppas.md) 15 | * [Tutorial](tutorial.md) 16 | 17 | *Source: https://github.com/arut/nginx-rtmp-module/wiki* 18 | 19 | ## Updates 20 | 21 | * Directives 22 | * Notify 23 | * [on_playlist](directives.md#on_playlist) 24 | * [notify_send_redirect](directives.md#notify_send_redirect) 25 | * Client Caching 26 | * [hls_allow_client_cache](directives.md#hls_allow_client_cache) 27 | * Dash MPD generation 28 | * [dash_clock_compensation](directives.md#dash_clock_compensation) 29 | * [dash_clock_helper_uri](directives.md#dash_clock_helper_uri) 30 | -------------------------------------------------------------------------------- /doc/control_modul.md: -------------------------------------------------------------------------------- 1 | # Control module 2 | 3 | Control module is HTTP module which makes it possible to control rtmp module from outside using HTTP protocol. Here's an example of how to enable control. 4 | ```sh 5 | http { 6 | ... 7 | server { 8 | listen 8080; 9 | server_name localhost; 10 | .... 11 | location /control { 12 | rtmp_control all; 13 | } 14 | } 15 | } 16 | ``` 17 | 18 | There are several sub-modules within control module each controlling a different feature. 19 | 20 | # Record 21 | This sub-module starts and stops recordings created with _manual_ flag. 22 | Syntax: 23 | ```sh 24 | http://server.com/control/record/start|stop?srv=SRV&app=APP&name=NAME&rec=REC 25 | ``` 26 | 27 | * srv=SRV - optional server{} block number within rtmp{} block, default to first server{} block 28 | * app=APP - required application name 29 | * name=NAME - required stream name 30 | * rec=REC - optional recorder name, defaults to root (unnamed) recorder 31 | 32 | Example 33 | ```sh 34 | rtmp { 35 | server { 36 | listen 1935; 37 | application myapp { 38 | live on; 39 | recorder rec1 { 40 | record all manual; 41 | record_suffix all.flv; 42 | record_path /tmp/rec; 43 | record_unique on; 44 | } 45 | } 46 | } 47 | } 48 | ``` 49 | 50 | Publish the stream with the following command 51 | ```sh 52 | $ ffmpeg -i http://someserver.com/mychannel.ts -c:v copy -c:a nellymoser -ar 44100 -ac 1 -f flv rtmp://localhost/myapp/mystream 53 | ``` 54 | 55 | Use the following commands to start and stop recording 56 | ```sh 57 | $ curl "http://localhost:8080/control/record/start?app=myapp&name=mystream&rec=rec1" 58 | § curl "http://localhost:8080/control/record/stop?app=myapp&name=mystream&rec=rec1" 59 | ``` 60 | 61 | if the record start/stop request returns nothing sometimes, you should check if you use multi workers. one worker works great. 62 | 63 | # Drop 64 | This sub-module provides a simple way to drop client connection. 65 | Syntax: 66 | ```sh 67 | http://server.com/control/drop/publisher|subscriber|client? 68 | srv=SRV&app=APP&name=NAME&addr=ADDR&clientid=CLIENTID 69 | ``` 70 | 71 | * srv, app, name - the same as above 72 | * addr - optional client address (the same as returned by rtmp_stat) 73 | * clientid - optional nginx client id (displayed in log and stat) 74 | 75 | The first method ```drop/publisher``` drops publisher connection. The second ```drop/client``` drops every connection matching ```addr``` argument or all clients (including publisher) if ```addr``` is not specified. 76 | 77 | Examples 78 | ```sh 79 | $ curl http://localhost:8080/control/drop/publisher?app=myapp&name=mystream 80 | $ curl http://localhost:8080/control/drop/client?app=myapp&name=mystream 81 | $ curl http://localhost:8080/control/drop/client?app=myapp&name=mystream&addr=192.168.0.1 82 | $ curl http://localhost:8080/control/drop/client?app=myapp&name=mystream&clientid=1 83 | ``` 84 | 85 | # Redirect 86 | Redirect play/publish client to a new stream. 87 | Syntax: 88 | ```sh 89 | http://server.com/control/redirect/publisher|subscriber|client? 90 | srv=SRV&app=APP&name=NAME&addr=ADDR&clientid=CLIENTID&newname=NEWNAME 91 | ``` 92 | 93 | * srv, app, name, addr, clients - the same as above 94 | * newname - new stream name to redirect to 95 | -------------------------------------------------------------------------------- /doc/debug_log.md: -------------------------------------------------------------------------------- 1 | # Debug log 2 | 3 | In case you need to solve a streaming problem you might need to watch debug log. 4 | For that configure nginx with *--with-debug* flag. 5 | ```sh 6 | $ cd nginx-X.Y.Z 7 | $ ./configure --add-module=/path/to/nginx-rtmp-module --with-debug ... 8 | ``` 9 | 10 | After compiling set nginx error.log level to *debug* in nginx.conf 11 | ```sh 12 | error_log logs/error.log debug; 13 | ``` 14 | 15 | After that you will have _a lot_ of debug info in error.log. Please grep 16 | what your problem relates to (exec, notify etc) and post to [nginx-rtmp google group](https://groups.google.com/group/nginx-rtmp) to help with solving it. -------------------------------------------------------------------------------- /doc/examples.md: -------------------------------------------------------------------------------- 1 | # Exampled 2 | 3 | ### Simple Video-on-Demand 4 | 5 | ```sh 6 | rtmp { 7 | server { 8 | listen 1935; 9 | application vod { 10 | play /var/flvs; 11 | } 12 | } 13 | } 14 | ``` 15 | 16 | ### Simple live broadcast service 17 | ```sh 18 | rtmp { 19 | server { 20 | listen 1935; 21 | application live { 22 | live on; 23 | } 24 | } 25 | } 26 | ``` 27 | 28 | ### Re-translate remote stream 29 | ```sh 30 | rtmp { 31 | server { 32 | listen 1935; 33 | application tv { 34 | live on; 35 | pull rtmp://cdn.example.com:443/programs/main pageUrl=http://www.example.com/index.html name=maintv; 36 | } 37 | } 38 | } 39 | 40 | ### Re-translate remote stream with HLS support 41 | ```sh 42 | rtmp { 43 | server { 44 | listen 1935; 45 | application tv { 46 | live on; 47 | hls on; 48 | hls_path /tmp/tv2; 49 | hls_fragment 15s; 50 | 51 | pull rtmp://tv2.example.com:443/root/new name=tv2; 52 | } 53 | } 54 | } 55 | http { 56 | server { 57 | listen 80; 58 | location /tv2 { 59 | alias /tmp/tv2; 60 | } 61 | } 62 | } 63 | ``` 64 | 65 | ### Stream your X screen through RTMP 66 | ```sh 67 | $ ffmpeg -f x11grab -follow_mouse centered -r 25 -s cif -i :0.0 -f flv rtmp://localhost/myapp/screen 68 | ``` 69 | -------------------------------------------------------------------------------- /doc/exec_wrapper_in_bash.md: -------------------------------------------------------------------------------- 1 | # Exec wrapper in bash 2 | 3 | You can write exec wrapper in any language. However you should pay attention to termination process. When publisher closes the stream all executed processed get terminated. If you specify wrapper in ```exec``` directive instead of real ffmpeg then you might end up with your ffmpeg still alive and orphaned until it times out reading input data. 4 | 5 | The solution is using signal traps. Here's an example of such wrapper in bash. 6 | 7 | ```sh 8 | #!/bin/bash 9 | 10 | on_die () 11 | { 12 | # kill all children 13 | pkill -KILL -P $$ 14 | } 15 | 16 | trap 'on_die' TERM 17 | ffmpeg -i rtmp://localhost/myapp/$1 -c copy -f flv rtmp://localhost/myapp2/$1 & 18 | wait 19 | ``` 20 | 21 | The script registers SIGTERM handler which terminates child ffmpeg. Default signal sent by nginx-rtmp is SIGKILL which cannot be caught. For the above script to behave as expected you need to change exec kill signal with ```exec_kill_signal``` directive. It accept numeric or symbolic signal name (for POSIX.1-1990 signals). Here's example application. 22 | ```sh 23 | application myapp { 24 | live on; 25 | 26 | exec /var/scripts/exec_wrapper.sh $name; 27 | exec_kill_signal term; 28 | } 29 | 30 | application myapp2 { 31 | live on; 32 | } 33 | ``` 34 | -------------------------------------------------------------------------------- /doc/faq.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | ####RTMP stream is not played normally in IE, stream stops after several seconds. 4 | 5 | Add this directive to fix the problem 6 | ```sh 7 | wait_video on; 8 | ``` 9 | 10 | ####I use `pull` directive to get stream from remote location. That works for RTMP clients but does not work for HLS. 11 | 12 | Currently HLS clients do not trigger any events. You cannot pull or exec when HLS client connects to server. However you can use static directives `exec_static`, `pull ... static` to pull the stream always. 13 | 14 | ####Seek does not work with flv files recorded by the module. 15 | 16 | To make the files seekable add flv metadata with external software like yamdi, flvmeta or ffmpeg. 17 | ```sh 18 | exec_record_done yamdi -i $path -o /var/videos/$basename; 19 | ``` 20 | 21 | ####Published stream is missing from stats page after some time and clients fail to connect 22 | 23 | Check if you use multiple workers in nginx (`worker_processes`). In such case you have to enable: 24 | ```sh 25 | rtmp_auto_push on; 26 | ``` 27 | -------------------------------------------------------------------------------- /doc/getting_number_of_subscribers.md: -------------------------------------------------------------------------------- 1 | # Getting number of subscribers 2 | 3 | There's an easy way to display number of clients watching the stream. You need to 4 | 5 | Set up statistics page at location `/stat` 6 | ```sh 7 | location /stat { 8 | rtmp_stat all; 9 | allow 127.0.0.1; 10 | } 11 | ``` 12 | 13 | Create a simple xsl stylesheet `nclients.xsl` extracting number of stream subscribers 14 | ```xsl 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ``` 28 | 29 | Set up a location returning number of suscribers 30 | ```sh 31 | location /nclients { 32 | proxy_pass http://127.0.0.1/stat; 33 | xslt_stylesheet /www/nclients.xsl app='$arg_app' name='$arg_name'; 34 | add_header Refresh "3; $request_uri"; 35 | } 36 | ``` 37 | 38 | Use HTTP request `http://myserver.com/nclients?app=myapp&name=mystream` to get the number of stream subscribers. This number will be automatically refreshed every 3 seconds when opened in browser or iframe. 39 | -------------------------------------------------------------------------------- /doc/getting_started.md: -------------------------------------------------------------------------------- 1 | # Getting started with nginx rtmp 2 | 3 | ## Download, build and install 4 | 5 | CD to build directory (home) 6 | ```sh 7 | $ cd /usr/build 8 | ``` 9 | 10 | Download & unpack latest nginx-rtmp (you can also use http) 11 | ```sh 12 | $ git clone git://github.com/arut/nginx-rtmp-module.git 13 | ``` 14 | 15 | Download & unpack nginx (you can also use svn) 16 | 17 | ```sh 18 | $ wget http://nginx.org/download/nginx-1.2.4.tar.gz 19 | $ tar xzf nginx-1.2.4.tar.gz 20 | $ cd nginx-1.2.4 21 | ``` 22 | 23 | Build nginx with nginx-rtmp 24 | ```sh 25 | $ ./configure --add-module=/usr/build/nginx-rtmp-module 26 | $ make 27 | $ make install 28 | ``` 29 | 30 | For nginx 1.3.4-1.5.0 more options are needed 31 | ```sh 32 | $ ./configure --add-module=/usr/build/nginx-rtmp-module --with-http_ssl_module 33 | $ make 34 | $ make install 35 | ``` 36 | 37 | ## Set up live streaming 38 | 39 | To set up RTMP support you need to add `rtmp{}` section to `nginx.conf` (can be found in PREFIX/conf/nginx.conf). Stock `nginx.conf` contains only `http{}` section. 40 | 41 | Use this `nginx.conf` instead of stock config: 42 | ```sh 43 | #user nobody; 44 | worker_processes 1; 45 | 46 | error_log logs/error.log debug; 47 | 48 | events { 49 | worker_connections 1024; 50 | } 51 | 52 | http { 53 | include mime.types; 54 | default_type application/octet-stream; 55 | 56 | sendfile on; 57 | keepalive_timeout 65; 58 | 59 | server { 60 | listen 8080; 61 | server_name localhost; 62 | 63 | # sample handlers 64 | #location /on_play { 65 | # if ($arg_pageUrl ~* localhost) { 66 | # return 201; 67 | # } 68 | # return 202; 69 | #} 70 | #location /on_publish { 71 | # return 201; 72 | #} 73 | 74 | #location /vod { 75 | # alias /var/myvideos; 76 | #} 77 | 78 | # rtmp stat 79 | location /stat { 80 | rtmp_stat all; 81 | rtmp_stat_stylesheet stat.xsl; 82 | } 83 | location /stat.xsl { 84 | # you can move stat.xsl to a different location 85 | root /usr/build/nginx-rtmp-module; 86 | } 87 | 88 | # rtmp control 89 | location /control { 90 | rtmp_control all; 91 | } 92 | error_page 500 502 503 504 /50x.html; 93 | location = /50x.html { 94 | root html; 95 | } 96 | } 97 | } 98 | rtmp { 99 | server { 100 | listen 1935; 101 | ping 30s; 102 | notify_method get; 103 | 104 | application myapp { 105 | live on; 106 | 107 | # sample play/publish handlers 108 | #on_play http://localhost:8080/on_play; 109 | #on_publish http://localhost:8080/on_publish; 110 | 111 | # sample recorder 112 | #recorder rec1 { 113 | # record all; 114 | # record_interval 30s; 115 | # record_path /tmp; 116 | # record_unique on; 117 | #} 118 | 119 | # sample HLS 120 | #hls on; 121 | #hls_path /tmp/hls; 122 | #hls_sync 100ms; 123 | } 124 | 125 | # Video on demand 126 | #application vod { 127 | # play /var/Videos; 128 | #} 129 | 130 | # Video on demand over HTTP 131 | #application vod_http { 132 | # play http://localhost:8080/vod/; 133 | #} 134 | } 135 | } 136 | ``` 137 | 138 | ## Statistics 139 | 140 | Navigate your browser to `http://localhost:8080/stat` to see current 141 | streaming statistics, connected clients, bandwidth etc. 142 | 143 | ## Publishing with ffmpeg 144 | 145 | The easiest way to publish live video stream is using ffmpeg (or avconv). 146 | It's already installed on most systems and easy to install on others. 147 | 148 | RTMP supports only a limited number of codecs. The most popular RTMP video 149 | codecs are H264, Sorenson-H263 (aka flv) and audio codecs AAC, MP3, 150 | Nellymoser, Speex. If your video is encoded with these codecs 151 | (the most common pair is H264/AAC) then you do not need any conversion. 152 | Otherwise you need to convert video to one of supported codecs. 153 | 154 | We'll stream test file `/var/videos/test.mp4` to server with ffmpeg. 155 | 156 | Streaming without conversion (given `test.mp4` codecs are compatible with RTMP) 157 | ```sh 158 | $ ffmpeg -re -i /var/Videos/test.mp4 -c copy -f flv rtmp://localhost/myapp/mystream 159 | ``` 160 | 161 | Streaming and encoding audio (AAC) and video (H264), need `libx264` and `libfaac` 162 | ```sh 163 | $ ffmpeg -re -i /var/Videos/test.mp4 -c:v libx264 -c:a libfaac -ar 44100 -ac 1 -f flv rtmp://localhost/myapp/mystream 164 | ``` 165 | 166 | Streaming and encoding audio (MP3) and video (H264), need `libx264` and `libmp3lame` 167 | ```sh 168 | $ ffmpeg -re -i /var/Videos/test.mp4 -c:v libx264 -c:a libmp3lame -ar 44100 -ac 1 -f flv rtmp://localhost/myapp/mystream 169 | ``` 170 | 171 | Streaming and encoding audio (Nellymoser) and video (Sorenson H263) 172 | ```sh 173 | $ ffmpeg -re -i /var/Videos/test.mp4 -c:v flv -c:a nellymoser -ar 44100 -ac 1 -f flv rtmp://localhost/myapp/mystream 174 | ``` 175 | 176 | ## Publishing video from webcam 177 | ```sh 178 | $ ffmpeg -f video4linux2 -i /dev/video0 -c:v libx264 -an -f flv rtmp://localhost/myapp/mystream 179 | ``` 180 | 181 | ## Playing with ffplay 182 | ```sh 183 | $ ffplay rtmp://localhost/myapp/mystream 184 | ``` 185 | 186 | ## Publishing and playing with flash 187 | 188 | See `test/rtmp-publisher` directory for test flash applets and html. 189 | -------------------------------------------------------------------------------- /doc/installing_in_gentoo.md: -------------------------------------------------------------------------------- 1 | # Installing in Gentoo 2 | 3 | ## Download module source code 4 | You have many options: 5 | * Get the zip at https://github.com/arut/nginx-rtmp-module/archive/master.zip 6 | * Or much better, do a git clone (see options in top of https://github.com/arut/nginx-rtmp-module) 7 | * Or get an ebuild from [fem-overlay](http://subversion.fem.tu-ilmenau.de/repository/fem-overlay/trunk/www-servers/nginx/nginx-1.2.5-r1.ebuild). And set the USE flag "nginx_modules_rtmp" or "nginx_modules_rtmp_hls". 8 | 9 | ## Emerge nginx with nginx-rtmp-module 10 | > NGINX_ADD_MODULES="/path/to/nginx-rtmp-module" emerge -va nginx 11 | 12 | Replace `/path/to/` with the actual module's source path. 13 | You can add with this method any number of custom modules. 14 | 15 | To make this change permanent see: 16 | http://wiki.gentoo.org/wiki/Knowledge_Base:Overriding_environment_variables_per_package 17 | 18 | ## Configure nginx 19 | Don't forget to include a rtmp section inside your nginx configuration file located at `/etc/nginx/nginx.conf`. 20 | 21 | See: 22 | * [Getting started](getting_started.md) We already have done _Download, build and install_ Gentoo style ;-) 23 | * [More Examples](examples.md) 24 | * [Reference of all directives](directives.md) 25 | -------------------------------------------------------------------------------- /doc/installing_ubuntu_using_ppas.md: -------------------------------------------------------------------------------- 1 | # Installing on Ubuntu using PPAs 2 | ```sh 3 | $ sudo apt-get install dpkg-dev 4 | $ sudo apt-get source nginx 5 | $ cd /usr/src/nginx 6 | $ sudo git clone https://github.com/arut/nginx-rtmp-module.git 7 | $ cd nginx-[version-number] 8 | $ sudo vi debian/rules 9 | ``` 10 | 11 | Edit the rules and at then end of the add-modules configuration string add 12 | ```sh 13 | --add-module=/usr/src/nginx/nginx-rtmp-module \ 14 | ``` 15 | 16 | If installing for the first time build nginx dependancies. 17 | ```sh 18 | $ sudo apt-get build-dep nginx 19 | $ dpkg-buildpackage -b 20 | ``` 21 | 22 | (wait for a while while it builds... a really long while... like you might want to go grab a meal) 23 | 24 | ```sh 25 | $ cd .. && sudo dpkg --install nginx-common_1.3.13-1chl1~quantal1_all.deb nginx-full_1.3.13-1chl1~quantal1_amd64.deb 26 | $ sudo service nginx status 27 | $ sudo service nginx start (if nginx isn't running) 28 | ``` 29 | 30 | [Source](http://serverfault.com/questions/227480/installing-optional-nginx-modules-with-apt-get) 31 | -------------------------------------------------------------------------------- /doc/tutorial.md: -------------------------------------------------------------------------------- 1 | # Tutorial 2 | 3 | [This article is not finished yet] 4 | 5 | ## RTMP 6 | RTMP is a proprietary protocol developed by Adobe (Macromedia) for use 7 | in flash player. Until 2009 it had no public specification. 8 | A number of third-party RTMP-related products started in that period 9 | were based on the results of reverse engineering. In 2009 10 | [RTMP specification](http://www.adobe.com/devnet/rtmp.html) has been 11 | published which made developing such applications easier. However 12 | the spec is not full and misses significant issues concerning streaming H264. 13 | 14 | ## System requirements 15 | The module has been tested on Linux x86-family platforms. 16 | However it should work on FreeBSD too. 17 | 18 | ## Licence 19 | The module is distributed under BSD license. 20 | 21 | ## Building NGINX with the module 22 | Building is pretty obvious. Just cd to nginx source directory 23 | and configure nginx this way: 24 | ```sh 25 | $ ./configure --add-module=/path/to/nginx-rtmp-module 26 | ``` 27 | 28 | Then `make` and `make install`. 29 | 30 | ## Configuration 31 | 32 | ## Simple live application 33 | Simple live application configuration: 34 | ```sh 35 | application live { 36 | 37 | live on; 38 | 39 | } 40 | ``` 41 | 42 | You can add access list control: 43 | ```sh 44 | application live { 45 | 46 | live on; 47 | 48 | allow publish 127.0.0.1; 49 | deny publish all; 50 | allow play all; 51 | 52 | } 53 | ``` 54 | 55 | And you can add record support for live streams: 56 | ```sh 57 | application live { 58 | 59 | live on; 60 | 61 | allow publish 127.0.0.1; 62 | deny publish all; 63 | allow play all; 64 | 65 | record all; 66 | record_path /path/to/record/dir; 67 | record_max_size 100M; 68 | record_unique off; 69 | 70 | } 71 | ``` 72 | 73 | ## HLS (HTTP Live Streaming) 74 | 75 | ## Choosing flash player 76 | To watch RTMP stream in browser one should either develop 77 | flash application for that or use one of available flash 78 | players. The most popular players which are proved to have 79 | no problems with the module are: 80 | 81 | * [JWPlayer](http://www.longtailvideo.com/) 82 | * [FlowPlayer](http://flowplayer.org/) 83 | * [Strobe Media Playback](http://www.osmf.org/strobe_mediaplayback.html) 84 | * [Clappr](https://github.com/globocom/clappr) 85 | 86 | Old versions of JWPlayer (<=4.4) supported capturing video 87 | from webcam. You can find that version in test/ subdirectory. 88 | However audio is not captured by this version of player. 89 | Recent free versions of JWPlayer have no capture capability at 90 | all. 91 | 92 | ## Transcoding streams 93 | You can use exec directive and ffmpeg for transcoding streams. For example: 94 | ```sh 95 | application big { 96 | live on; 97 | exec /usr/bin/ffmpeg -re -i rtmp://localhost:1935/$app/$name -vcodec flv -acodec copy -s 32x32 -f flv rtmp://localhost:1935/small/${name}; 98 | } 99 | application small { 100 | live on; 101 | } 102 | ``` 103 | 104 | ## Video on demand 105 | 106 | ## Distributed streaming 107 | 108 | ## Notifications & access control 109 | 110 | ## Statistics 111 | 112 | ## Verifying session 113 | 114 | ## Utilizing multi-core CPUs 115 | -------------------------------------------------------------------------------- /hls/ngx_rtmp_mpegts.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include "ngx_rtmp_mpegts.h" 10 | #include "ngx_rtmp_mpegts_crc.h" 11 | 12 | #include "ngx_rtmp_codec_module.h" 13 | 14 | static u_char ngx_rtmp_mpegts_header[] = { 15 | 16 | /* https://en.wikipedia.org/wiki/MPEG_transport_stream#Packet */ 17 | 18 | /* TS Header */ 19 | 0x47, // Sync byte 20 | 0x40, 0x00, // TEI(1) + PUS(1) + TP(1) + PID(13) 21 | 0x10, // TSC(2) + AFF(1) + PF(1) + CC(4) 22 | 0x00, // adaption_field_length(8) 23 | 24 | /* PAT */ 25 | 0x00, // table_id(8) 26 | 0xb0, 0x0d, // 1011b(4) + section_length(12) 27 | 0x00, 0x01, // transport_stream_id(16) 28 | 0xc1, 0x00, 0x00, // 11b(2) + VN(5) + CNI(1), section_no(8), last_section_no(8) 29 | /* PAT program loop */ 30 | 0x00, 0x01, 0xef, 0xff, // program_number(16), reserved(3) + program_map_pid(13) 31 | /* PAT crc (CRC-32-MPEG2) */ 32 | 0x36, 0x90, 0xe2, 0x3d, // !!! Needs to be recalculated each time any bit in PAT is modified (which we dont do at the moment) !!! 33 | 34 | /* stuffing 167 bytes */ 35 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 36 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 37 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 38 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 39 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 40 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 41 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 42 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 43 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 44 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 45 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 46 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 47 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 48 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 49 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 50 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 51 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 52 | 53 | /* TS Header */ 54 | 0x47, // Sync byte 55 | 0x4f, 0xff, // TEI(1) + PUS(1) + TP(1) + PID(13) 56 | 0x10, // TSC(2) + AFF(1) + PF(1) + CC(4) 57 | 0x00, // adaption_field_length(8) 58 | 59 | /* PMT */ 60 | 0x02, // table_id(8) 61 | 0xb0, 0x12, // 1011b(4) + section_length(12) (section length set below. Ignore this value in here) 62 | 0x00, 0x01, // program_number(16) 63 | 0xc1, 0x00, 0x00, // 11b(2) + VN(5) + CNI(1), section_no(8), last_section_no(8) 64 | 0xe1, 0x00, // reserved(3) + PCR_PID(13) 65 | 0xf0, 0x00, // reserved(4) + program_info_length(12) 66 | 67 | /* PMT component loop, looped through when writing header */ 68 | /* Max size of 14 bytes */ 69 | /* Also includes the PMT CRC, calculated dynamically */ 70 | 0xff, 0xff, 0xff, 0xff, 0xff, 71 | 0xff, 0xff, 0xff, 0xff, 0xff, 72 | 0xff, 0xff, 0xff, 0xff, 73 | 74 | /* stuffing 157 bytes */ 75 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 76 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 77 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 78 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 79 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 80 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 81 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 82 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 83 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 84 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 85 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 86 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 87 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 88 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 89 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 90 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 91 | }; 92 | 93 | static u_char ngx_rtmp_mpegts_header_h264[] = { 94 | //H.264 Video, PID 0x100 95 | 0x1b, // stream_type(8) 96 | 0xe1, 0x00, // reserved(3) + elementary_PID(13) 97 | 0xf0, 0x00 // reserved(4) + ES_info_length(12) 98 | }; 99 | 100 | static u_char ngx_rtmp_mpegts_header_mp3[] = { 101 | //MP3 Audio, PID 0x101 102 | 0x03, // stream_type(8) 103 | 0xe1, 0x01, // reserved(3) + elementary_PID(13) 104 | 0xf0, 0x00 // reserved(4) + ES_info_length(12) 105 | }; 106 | 107 | static u_char ngx_rtmp_mpegts_header_aac[] = { 108 | //ADTS AAC Audio, PID 0x101 109 | 0x0f, // stream_type(8) 110 | 0xe1, 0x01, // reserved(3) + elementary_PID(13) 111 | 0xf0, 0x00 // reserved(4) + ES_info_length(12) 112 | }; 113 | 114 | #define NGX_RTMP_MPEGTS_PMT_CRC_START_OFFSET 193 115 | #define NGX_RTMP_MPEGTS_PMT_CRC_MIN_LENGTH 12 116 | #define NGX_RTMP_MPEGTS_PMT_SECTION_LENGTH_OFFSET 195 117 | #define NGX_RTMP_MPEGTS_PMT_LOOP_OFFSET 205 118 | #define NGX_RTMP_MPEGTS_PID_SIZE 5 119 | 120 | /* 700 ms PCR delay */ 121 | #define NGX_RTMP_HLS_DELAY 63000 122 | 123 | 124 | static ngx_int_t 125 | ngx_rtmp_mpegts_write_file(ngx_rtmp_mpegts_file_t *file, u_char *in, 126 | size_t in_size) 127 | { 128 | u_char *out; 129 | size_t out_size, n; 130 | ssize_t rc; 131 | 132 | static u_char buf[1024]; 133 | 134 | if (!file->encrypt) { 135 | ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0, 136 | "mpegts: write %uz bytes", in_size); 137 | 138 | rc = ngx_write_fd(file->fd, in, in_size); 139 | if (rc < 0) { 140 | return NGX_ERROR; 141 | } 142 | 143 | return NGX_OK; 144 | } 145 | 146 | /* encrypt */ 147 | 148 | ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0, 149 | "mpegts: write %uz encrypted bytes", in_size); 150 | 151 | out = buf; 152 | out_size = sizeof(buf); 153 | 154 | if (file->size > 0 && file->size + in_size >= 16) { 155 | ngx_memcpy(file->buf + file->size, in, 16 - file->size); 156 | 157 | in += 16 - file->size; 158 | in_size -= 16 - file->size; 159 | 160 | AES_cbc_encrypt(file->buf, out, 16, &file->key, file->iv, AES_ENCRYPT); 161 | 162 | out += 16; 163 | out_size -= 16; 164 | 165 | file->size = 0; 166 | } 167 | 168 | for ( ;; ) { 169 | n = in_size & ~0x0f; 170 | 171 | if (n > 0) { 172 | if (n > out_size) { 173 | n = out_size; 174 | } 175 | 176 | AES_cbc_encrypt(in, out, n, &file->key, file->iv, AES_ENCRYPT); 177 | 178 | in += n; 179 | in_size -= n; 180 | 181 | } else if (out == buf) { 182 | break; 183 | } 184 | 185 | rc = ngx_write_fd(file->fd, buf, out - buf + n); 186 | if (rc < 0) { 187 | return NGX_ERROR; 188 | } 189 | 190 | out = buf; 191 | out_size = sizeof(buf); 192 | } 193 | 194 | if (in_size) { 195 | ngx_memcpy(file->buf + file->size, in, in_size); 196 | file->size += in_size; 197 | } 198 | 199 | return NGX_OK; 200 | } 201 | 202 | 203 | static ngx_int_t 204 | ngx_rtmp_mpegts_write_header(ngx_rtmp_mpegts_file_t *file, ngx_rtmp_codec_ctx_t *codec_ctx, ngx_uint_t mpegts_cc) 205 | { 206 | ngx_int_t next_pid_offset = 0; //Used to track the number of PIDs we have and the offset in 5-byte chunks 207 | 208 | //MPEG-TS CC is 4 bits long. Need to truncate it here. 209 | mpegts_cc %= 0x0f; 210 | // And then put it in the headers 211 | ngx_rtmp_mpegts_header[3] = (ngx_rtmp_mpegts_header[3] & 0xf0) + (u_char)mpegts_cc; 212 | ngx_rtmp_mpegts_header[191] = (ngx_rtmp_mpegts_header[191] & 0xf0) + (u_char)mpegts_cc; 213 | 214 | //ngx_rtmp_mpegts_header 215 | 216 | if (codec_ctx->video_codec_id) 217 | { 218 | //Put h264 PID in the PMT 219 | ngx_memcpy(ngx_rtmp_mpegts_header+NGX_RTMP_MPEGTS_PMT_LOOP_OFFSET+next_pid_offset, ngx_rtmp_mpegts_header_h264, NGX_RTMP_MPEGTS_PID_SIZE); 220 | 221 | next_pid_offset += NGX_RTMP_MPEGTS_PID_SIZE; 222 | } 223 | 224 | if (codec_ctx->audio_codec_id){ 225 | //Put Audio PID in the PMT 226 | if (codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC) { 227 | ngx_memcpy(ngx_rtmp_mpegts_header+NGX_RTMP_MPEGTS_PMT_LOOP_OFFSET+next_pid_offset, ngx_rtmp_mpegts_header_aac, NGX_RTMP_MPEGTS_PID_SIZE); 228 | } 229 | else 230 | { 231 | ngx_memcpy(ngx_rtmp_mpegts_header+NGX_RTMP_MPEGTS_PMT_LOOP_OFFSET+next_pid_offset, ngx_rtmp_mpegts_header_mp3, NGX_RTMP_MPEGTS_PID_SIZE); 232 | } 233 | next_pid_offset += NGX_RTMP_MPEGTS_PID_SIZE; 234 | } 235 | 236 | //Set section length of PMT 237 | //PMT is 13 bytes long without any programs in it. Add this in 238 | ngx_rtmp_mpegts_header[NGX_RTMP_MPEGTS_PMT_SECTION_LENGTH_OFFSET] = 13 + next_pid_offset; 239 | 240 | //Calculate CRC 241 | ngx_rtmp_mpegts_crc_t crc = ngx_rtmp_mpegts_crc_init(); 242 | crc = ngx_rtmp_mpegts_crc_update(crc, ngx_rtmp_mpegts_header+NGX_RTMP_MPEGTS_PMT_CRC_START_OFFSET, NGX_RTMP_MPEGTS_PMT_CRC_MIN_LENGTH+next_pid_offset); 243 | crc = ngx_rtmp_mpegts_crc_finalize(crc); 244 | 245 | ngx_rtmp_mpegts_header[NGX_RTMP_MPEGTS_PMT_LOOP_OFFSET+next_pid_offset] = (crc >> 24) & 0xff; 246 | ngx_rtmp_mpegts_header[NGX_RTMP_MPEGTS_PMT_LOOP_OFFSET+next_pid_offset+1] = (crc >> 16) & 0xff; 247 | ngx_rtmp_mpegts_header[NGX_RTMP_MPEGTS_PMT_LOOP_OFFSET+next_pid_offset+2] = (crc >> 8) & 0xff; 248 | ngx_rtmp_mpegts_header[NGX_RTMP_MPEGTS_PMT_LOOP_OFFSET+next_pid_offset+3] = crc & 0xff; 249 | 250 | return ngx_rtmp_mpegts_write_file(file, ngx_rtmp_mpegts_header, sizeof(ngx_rtmp_mpegts_header)); 251 | } 252 | 253 | 254 | static u_char * 255 | ngx_rtmp_mpegts_write_pcr(u_char *p, uint64_t pcr) 256 | { 257 | *p++ = (u_char) (pcr >> 25); 258 | *p++ = (u_char) (pcr >> 17); 259 | *p++ = (u_char) (pcr >> 9); 260 | *p++ = (u_char) (pcr >> 1); 261 | *p++ = (u_char) (pcr << 7 | 0x7e); 262 | *p++ = 0; 263 | 264 | return p; 265 | } 266 | 267 | 268 | static u_char * 269 | ngx_rtmp_mpegts_write_pts(u_char *p, ngx_uint_t fb, uint64_t pts) 270 | { 271 | ngx_uint_t val; 272 | 273 | val = fb << 4 | (((pts >> 30) & 0x07) << 1) | 1; 274 | *p++ = (u_char) val; 275 | 276 | val = (((pts >> 15) & 0x7fff) << 1) | 1; 277 | *p++ = (u_char) (val >> 8); 278 | *p++ = (u_char) val; 279 | 280 | val = (((pts) & 0x7fff) << 1) | 1; 281 | *p++ = (u_char) (val >> 8); 282 | *p++ = (u_char) val; 283 | 284 | return p; 285 | } 286 | 287 | 288 | ngx_int_t 289 | ngx_rtmp_mpegts_write_frame(ngx_rtmp_mpegts_file_t *file, 290 | ngx_rtmp_mpegts_frame_t *f, ngx_buf_t *b) 291 | { 292 | ngx_uint_t pes_size, header_size, body_size, in_size, stuff_size, flags; 293 | u_char packet[188], *p, *base; 294 | ngx_int_t first, rc; 295 | 296 | ngx_log_debug6(NGX_LOG_DEBUG_CORE, file->log, 0, 297 | "mpegts: pid=%ui, sid=%ui, pts=%uL, " 298 | "dts=%uL, key=%ui, size=%ui", 299 | f->pid, f->sid, f->pts, f->dts, 300 | (ngx_uint_t) f->key, (size_t) (b->last - b->pos)); 301 | 302 | first = 1; 303 | 304 | while (b->pos < b->last) { 305 | p = packet; 306 | 307 | f->cc++; 308 | 309 | *p++ = 0x47; 310 | *p++ = (u_char) (f->pid >> 8); 311 | 312 | if (first) { 313 | p[-1] |= 0x40; 314 | } 315 | 316 | *p++ = (u_char) f->pid; 317 | *p++ = 0x10 | (f->cc & 0x0f); /* payload */ 318 | 319 | if (first) { 320 | 321 | packet[3] |= 0x20; /* adaptation */ 322 | 323 | *p++ = 7; /* size */ 324 | *p++ = 0x50; /* random access + PCR */ 325 | 326 | p = ngx_rtmp_mpegts_write_pcr(p, f->dts - NGX_RTMP_HLS_DELAY); 327 | 328 | /* PES header */ 329 | 330 | *p++ = 0x00; 331 | *p++ = 0x00; 332 | *p++ = 0x01; 333 | *p++ = (u_char) f->sid; 334 | 335 | header_size = 5; 336 | flags = 0x80; /* PTS */ 337 | 338 | if (f->dts != f->pts) { 339 | header_size += 5; 340 | flags |= 0x40; /* DTS */ 341 | } 342 | 343 | pes_size = (b->last - b->pos) + header_size + 3; 344 | if (pes_size > 0xffff) { 345 | pes_size = 0; 346 | } 347 | 348 | *p++ = (u_char) (pes_size >> 8); 349 | *p++ = (u_char) pes_size; 350 | *p++ = 0x80; /* H222 */ 351 | *p++ = (u_char) flags; 352 | *p++ = (u_char) header_size; 353 | 354 | p = ngx_rtmp_mpegts_write_pts(p, flags >> 6, f->pts + 355 | NGX_RTMP_HLS_DELAY); 356 | 357 | if (f->dts != f->pts) { 358 | p = ngx_rtmp_mpegts_write_pts(p, 1, f->dts + 359 | NGX_RTMP_HLS_DELAY); 360 | } 361 | 362 | first = 0; 363 | } 364 | 365 | body_size = (ngx_uint_t) (packet + sizeof(packet) - p); 366 | in_size = (ngx_uint_t) (b->last - b->pos); 367 | 368 | if (body_size <= in_size) { 369 | ngx_memcpy(p, b->pos, body_size); 370 | b->pos += body_size; 371 | 372 | } else { 373 | stuff_size = (body_size - in_size); 374 | 375 | if (packet[3] & 0x20) { 376 | 377 | /* has adaptation */ 378 | 379 | base = &packet[5] + packet[4]; 380 | p = ngx_movemem(base + stuff_size, base, p - base); 381 | ngx_memset(base, 0xff, stuff_size); 382 | packet[4] += (u_char) stuff_size; 383 | 384 | } else { 385 | 386 | /* no adaptation */ 387 | 388 | packet[3] |= 0x20; 389 | p = ngx_movemem(&packet[4] + stuff_size, &packet[4], 390 | p - &packet[4]); 391 | 392 | packet[4] = (u_char) (stuff_size - 1); 393 | if (stuff_size >= 2) { 394 | packet[5] = 0; 395 | ngx_memset(&packet[6], 0xff, stuff_size - 2); 396 | } 397 | } 398 | 399 | ngx_memcpy(p, b->pos, in_size); 400 | b->pos = b->last; 401 | } 402 | 403 | rc = ngx_rtmp_mpegts_write_file(file, packet, sizeof(packet)); 404 | if (rc != NGX_OK) { 405 | return rc; 406 | } 407 | } 408 | 409 | return NGX_OK; 410 | } 411 | 412 | 413 | ngx_int_t 414 | ngx_rtmp_mpegts_init_encryption(ngx_rtmp_mpegts_file_t *file, 415 | u_char *key, size_t key_len, uint64_t iv) 416 | { 417 | if (AES_set_encrypt_key(key, key_len * 8, &file->key)) { 418 | return NGX_ERROR; 419 | } 420 | 421 | ngx_memzero(file->iv, 8); 422 | 423 | file->iv[8] = (u_char) (iv >> 56); 424 | file->iv[9] = (u_char) (iv >> 48); 425 | file->iv[10] = (u_char) (iv >> 40); 426 | file->iv[11] = (u_char) (iv >> 32); 427 | file->iv[12] = (u_char) (iv >> 24); 428 | file->iv[13] = (u_char) (iv >> 16); 429 | file->iv[14] = (u_char) (iv >> 8); 430 | file->iv[15] = (u_char) (iv); 431 | 432 | file->encrypt = 1; 433 | 434 | return NGX_OK; 435 | } 436 | 437 | 438 | ngx_int_t 439 | ngx_rtmp_mpegts_open_file(ngx_rtmp_mpegts_file_t *file, u_char *path, 440 | ngx_log_t *log, ngx_rtmp_codec_ctx_t *codec_ctx, ngx_uint_t mpegts_cc) 441 | { 442 | file->log = log; 443 | 444 | file->fd = ngx_open_file(path, NGX_FILE_WRONLY, NGX_FILE_TRUNCATE, 445 | NGX_FILE_DEFAULT_ACCESS); 446 | 447 | if (file->fd == NGX_INVALID_FILE) { 448 | ngx_log_error(NGX_LOG_ERR, log, ngx_errno, 449 | "hls: error creating fragment file"); 450 | return NGX_ERROR; 451 | } 452 | 453 | file->size = 0; 454 | 455 | if (ngx_rtmp_mpegts_write_header(file, codec_ctx, mpegts_cc) != NGX_OK) { 456 | ngx_log_error(NGX_LOG_ERR, log, ngx_errno, 457 | "hls: error writing fragment header"); 458 | ngx_close_file(file->fd); 459 | return NGX_ERROR; 460 | } 461 | 462 | return NGX_OK; 463 | } 464 | 465 | 466 | ngx_int_t 467 | ngx_rtmp_mpegts_close_file(ngx_rtmp_mpegts_file_t *file) 468 | { 469 | u_char buf[16]; 470 | ssize_t rc; 471 | 472 | if (file->encrypt) { 473 | ngx_memset(file->buf + file->size, 16 - file->size, 16 - file->size); 474 | 475 | AES_cbc_encrypt(file->buf, buf, 16, &file->key, file->iv, AES_ENCRYPT); 476 | 477 | rc = ngx_write_fd(file->fd, buf, 16); 478 | if (rc < 0) { 479 | return NGX_ERROR; 480 | } 481 | } 482 | 483 | ngx_close_file(file->fd); 484 | 485 | return NGX_OK; 486 | } 487 | -------------------------------------------------------------------------------- /hls/ngx_rtmp_mpegts.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #ifndef _NGX_RTMP_MPEGTS_H_INCLUDED_ 8 | #define _NGX_RTMP_MPEGTS_H_INCLUDED_ 9 | 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | 18 | typedef struct { 19 | ngx_fd_t fd; 20 | ngx_log_t *log; 21 | unsigned encrypt:1; 22 | unsigned size:4; 23 | u_char buf[16]; 24 | u_char iv[16]; 25 | AES_KEY key; 26 | } ngx_rtmp_mpegts_file_t; 27 | 28 | 29 | typedef struct { 30 | uint64_t pts; 31 | uint64_t dts; 32 | ngx_uint_t pid; 33 | ngx_uint_t sid; 34 | ngx_uint_t cc; 35 | unsigned key:1; 36 | } ngx_rtmp_mpegts_frame_t; 37 | 38 | 39 | ngx_int_t ngx_rtmp_mpegts_init_encryption(ngx_rtmp_mpegts_file_t *file, 40 | u_char *key, size_t key_len, uint64_t iv); 41 | ngx_int_t ngx_rtmp_mpegts_open_file(ngx_rtmp_mpegts_file_t *file, u_char *path, 42 | ngx_log_t *log, ngx_rtmp_codec_ctx_t *codec_ctx, ngx_uint_t mpegts_cc); 43 | ngx_int_t ngx_rtmp_mpegts_close_file(ngx_rtmp_mpegts_file_t *file); 44 | ngx_int_t ngx_rtmp_mpegts_write_frame(ngx_rtmp_mpegts_file_t *file, 45 | ngx_rtmp_mpegts_frame_t *f, ngx_buf_t *b); 46 | 47 | 48 | #endif /* _NGX_RTMP_MPEGTS_H_INCLUDED_ */ 49 | -------------------------------------------------------------------------------- /hls/ngx_rtmp_mpegts_crc.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file crc.c 3 | * Functions and types for CRC checks. 4 | * 5 | * Generated on Thu May 5 15:32:31 2016, 6 | * by pycrc v0.9, https://pycrc.org 7 | * using the configuration: 8 | * Width = 32 9 | * Poly = 0x04c11db7 10 | * Xor_In = 0xffffffff 11 | * ReflectIn = False 12 | * Xor_Out = 0x00000000 13 | * ReflectOut = False 14 | * Algorithm = table-driven 15 | *****************************************************************************/ 16 | #include "ngx_rtmp_mpegts_crc.h" /* include the header file generated with pycrc */ 17 | #include 18 | #include 19 | 20 | /** 21 | * Static table used for the table_driven implementation. 22 | *****************************************************************************/ 23 | static const ngx_rtmp_mpegts_crc_t ngx_rtmp_mpegts_crc_table[256] = { 24 | 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, 25 | 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 26 | 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, 27 | 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, 28 | 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 29 | 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 30 | 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, 31 | 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 32 | 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, 33 | 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 34 | 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 35 | 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, 36 | 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, 37 | 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 38 | 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 39 | 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, 40 | 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 41 | 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, 42 | 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, 43 | 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 44 | 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, 45 | 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, 46 | 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 47 | 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, 48 | 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 49 | 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 50 | 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, 51 | 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 52 | 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 53 | 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 54 | 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 55 | 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 56 | }; 57 | 58 | /** 59 | * Update the crc value with new data. 60 | * 61 | * \param crc The current crc value. 62 | * \param data Pointer to a buffer of \a data_len bytes. 63 | * \param data_len Number of bytes in the \a data buffer. 64 | * \return The updated crc value. 65 | *****************************************************************************/ 66 | ngx_rtmp_mpegts_crc_t ngx_rtmp_mpegts_crc_update(ngx_rtmp_mpegts_crc_t crc, const void *data, size_t data_len) 67 | { 68 | const unsigned char *d = (const unsigned char *)data; 69 | unsigned int tbl_idx; 70 | 71 | while (data_len--) { 72 | tbl_idx = ((crc >> 24) ^ *d) & 0xff; 73 | crc = (ngx_rtmp_mpegts_crc_table[tbl_idx] ^ (crc << 8)) & 0xffffffff; 74 | 75 | d++; 76 | } 77 | return crc & 0xffffffff; 78 | } 79 | 80 | 81 | -------------------------------------------------------------------------------- /hls/ngx_rtmp_mpegts_crc.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file crc.h 3 | * Functions and types for CRC checks. 4 | * 5 | * Generated on Thu May 5 15:32:22 2016, 6 | * by pycrc v0.9, https://pycrc.org 7 | * using the configuration: 8 | * Width = 32 9 | * Poly = 0x04c11db7 10 | * Xor_In = 0xffffffff 11 | * ReflectIn = False 12 | * Xor_Out = 0x00000000 13 | * ReflectOut = False 14 | * Algorithm = table-driven 15 | *****************************************************************************/ 16 | #ifndef _NGX_RTMP_MPEGTS_CRC_H_INCLUDED_ 17 | #define _NGX_RTMP_MPEGTS_CRC_H_INCLUDED_ 18 | 19 | 20 | #include 21 | #include 22 | 23 | #ifdef __cplusplus 24 | extern "C" { 25 | #endif 26 | 27 | 28 | /** 29 | * The definition of the used algorithm. 30 | * 31 | * This is not used anywhere in the generated code, but it may be used by the 32 | * application code to call algoritm-specific code, is desired. 33 | *****************************************************************************/ 34 | #define CRC_ALGO_TABLE_DRIVEN 1 35 | 36 | 37 | /** 38 | * The type of the CRC values. 39 | * 40 | * This type must be big enough to contain at least 32 bits. 41 | *****************************************************************************/ 42 | typedef uint_fast32_t ngx_rtmp_mpegts_crc_t; 43 | 44 | 45 | /** 46 | * Calculate the initial crc value. 47 | * 48 | * \return The initial crc value. 49 | *****************************************************************************/ 50 | static inline ngx_rtmp_mpegts_crc_t ngx_rtmp_mpegts_crc_init(void) 51 | { 52 | return 0xffffffff; 53 | } 54 | 55 | 56 | /** 57 | * Update the crc value with new data. 58 | * 59 | * \param crc The current crc value. 60 | * \param data Pointer to a buffer of \a data_len bytes. 61 | * \param data_len Number of bytes in the \a data buffer. 62 | * \return The updated crc value. 63 | *****************************************************************************/ 64 | ngx_rtmp_mpegts_crc_t ngx_rtmp_mpegts_crc_update(ngx_rtmp_mpegts_crc_t crc, const void *data, size_t data_len); 65 | 66 | 67 | /** 68 | * Calculate the final crc value. 69 | * 70 | * \param crc The current crc value. 71 | * \return The final crc value. 72 | *****************************************************************************/ 73 | static inline ngx_rtmp_mpegts_crc_t ngx_rtmp_mpegts_crc_finalize(ngx_rtmp_mpegts_crc_t crc) 74 | { 75 | return crc ^ 0x00000000; 76 | } 77 | 78 | 79 | #ifdef __cplusplus 80 | } /* closing brace for extern "C" */ 81 | #endif 82 | 83 | #endif /* _NGX_RTMP_MPEGTS_CRC_H_INCLUDED_ */ 84 | -------------------------------------------------------------------------------- /ngx_rtmp_access_module.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include "ngx_rtmp.h" 10 | #include "ngx_rtmp_cmd_module.h" 11 | 12 | 13 | static ngx_rtmp_publish_pt next_publish; 14 | static ngx_rtmp_play_pt next_play; 15 | 16 | 17 | #define NGX_RTMP_ACCESS_PUBLISH 0x01 18 | #define NGX_RTMP_ACCESS_PLAY 0x02 19 | 20 | 21 | static char * ngx_rtmp_access_rule(ngx_conf_t *cf, ngx_command_t *cmd, 22 | void *conf); 23 | static ngx_int_t ngx_rtmp_access_postconfiguration(ngx_conf_t *cf); 24 | static void * ngx_rtmp_access_create_app_conf(ngx_conf_t *cf); 25 | static char * ngx_rtmp_access_merge_app_conf(ngx_conf_t *cf, 26 | void *parent, void *child); 27 | 28 | 29 | typedef struct { 30 | in_addr_t mask; 31 | in_addr_t addr; 32 | ngx_uint_t deny; 33 | ngx_uint_t flags; 34 | } ngx_rtmp_access_rule_t; 35 | 36 | 37 | #if (NGX_HAVE_INET6) 38 | 39 | typedef struct { 40 | struct in6_addr addr; 41 | struct in6_addr mask; 42 | ngx_uint_t deny; 43 | ngx_uint_t flags; 44 | } ngx_rtmp_access_rule6_t; 45 | 46 | #endif 47 | 48 | 49 | typedef struct { 50 | ngx_array_t rules; /* array of ngx_rtmp_access_rule_t */ 51 | #if (NGX_HAVE_INET6) 52 | ngx_array_t rules6; /* array of ngx_rtmp_access_rule6_t */ 53 | #endif 54 | } ngx_rtmp_access_app_conf_t; 55 | 56 | 57 | static ngx_command_t ngx_rtmp_access_commands[] = { 58 | 59 | { ngx_string("allow"), 60 | NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE12, 61 | ngx_rtmp_access_rule, 62 | NGX_RTMP_APP_CONF_OFFSET, 63 | 0, 64 | NULL }, 65 | 66 | { ngx_string("deny"), 67 | NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE12, 68 | ngx_rtmp_access_rule, 69 | NGX_RTMP_APP_CONF_OFFSET, 70 | 0, 71 | NULL }, 72 | 73 | ngx_null_command 74 | }; 75 | 76 | 77 | static ngx_rtmp_module_t ngx_rtmp_access_module_ctx = { 78 | NULL, /* preconfiguration */ 79 | ngx_rtmp_access_postconfiguration, /* postconfiguration */ 80 | NULL, /* create main configuration */ 81 | NULL, /* init main configuration */ 82 | NULL, /* create server configuration */ 83 | NULL, /* merge server configuration */ 84 | ngx_rtmp_access_create_app_conf, /* create app configuration */ 85 | ngx_rtmp_access_merge_app_conf, /* merge app configuration */ 86 | }; 87 | 88 | 89 | ngx_module_t ngx_rtmp_access_module = { 90 | NGX_MODULE_V1, 91 | &ngx_rtmp_access_module_ctx, /* module context */ 92 | ngx_rtmp_access_commands, /* module directives */ 93 | NGX_RTMP_MODULE, /* module type */ 94 | NULL, /* init master */ 95 | NULL, /* init module */ 96 | NULL, /* init process */ 97 | NULL, /* init thread */ 98 | NULL, /* exit thread */ 99 | NULL, /* exit process */ 100 | NULL, /* exit master */ 101 | NGX_MODULE_V1_PADDING 102 | }; 103 | 104 | 105 | static void * 106 | ngx_rtmp_access_create_app_conf(ngx_conf_t *cf) 107 | { 108 | ngx_rtmp_access_app_conf_t *aacf; 109 | 110 | aacf = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_access_app_conf_t)); 111 | if (aacf == NULL) { 112 | return NULL; 113 | } 114 | 115 | if (ngx_array_init(&aacf->rules, cf->pool, 1, 116 | sizeof(ngx_rtmp_access_rule_t)) 117 | != NGX_OK) 118 | { 119 | return NULL; 120 | } 121 | 122 | #if (NGX_HAVE_INET6) 123 | if (ngx_array_init(&aacf->rules6, cf->pool, 1, 124 | sizeof(ngx_rtmp_access_rule6_t)) 125 | != NGX_OK) 126 | { 127 | return NULL; 128 | } 129 | #endif 130 | 131 | return aacf; 132 | } 133 | 134 | 135 | static ngx_int_t 136 | ngx_rtmp_access_merge_rules(ngx_array_t *prev, ngx_array_t *rules) 137 | { 138 | void *p; 139 | 140 | if (prev->nelts == 0) { 141 | return NGX_OK; 142 | } 143 | 144 | if (rules->nelts == 0) { 145 | *rules = *prev; 146 | return NGX_OK; 147 | } 148 | 149 | p = ngx_array_push_n(rules, prev->nelts); 150 | if (p == NULL) { 151 | return NGX_ERROR; 152 | } 153 | 154 | ngx_memcpy(p, prev->elts, prev->size * prev->nelts); 155 | 156 | return NGX_OK; 157 | } 158 | 159 | 160 | static char * 161 | ngx_rtmp_access_merge_app_conf(ngx_conf_t *cf, void *parent, void *child) 162 | { 163 | ngx_rtmp_access_app_conf_t *prev = parent; 164 | ngx_rtmp_access_app_conf_t *conf = child; 165 | 166 | if (ngx_rtmp_access_merge_rules(&prev->rules, &conf->rules) != NGX_OK) { 167 | return NGX_CONF_ERROR; 168 | } 169 | 170 | #if (NGX_HAVE_INET6) 171 | if (ngx_rtmp_access_merge_rules(&prev->rules6, &conf->rules6) != NGX_OK) { 172 | return NGX_CONF_ERROR; 173 | } 174 | #endif 175 | 176 | return NGX_CONF_OK; 177 | } 178 | 179 | 180 | static ngx_int_t 181 | ngx_rtmp_access_found(ngx_rtmp_session_t *s, ngx_uint_t deny) 182 | { 183 | if (deny) { 184 | ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, 185 | "access forbidden by rule"); 186 | return NGX_ERROR; 187 | } 188 | 189 | return NGX_OK; 190 | } 191 | 192 | 193 | static ngx_int_t 194 | ngx_rtmp_access_inet(ngx_rtmp_session_t *s, in_addr_t addr, ngx_uint_t flag) 195 | { 196 | ngx_uint_t i; 197 | ngx_rtmp_access_rule_t *rule; 198 | ngx_rtmp_access_app_conf_t *ascf; 199 | 200 | ascf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_access_module); 201 | 202 | rule = ascf->rules.elts; 203 | for (i = 0; i < ascf->rules.nelts; i++) { 204 | 205 | ngx_log_debug3(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, 206 | "access: %08XD %08XD %08XD", 207 | addr, rule[i].mask, rule[i].addr); 208 | 209 | if ((addr & rule[i].mask) == rule[i].addr && (flag & rule[i].flags)) { 210 | return ngx_rtmp_access_found(s, rule[i].deny); 211 | } 212 | } 213 | 214 | return NGX_OK; 215 | } 216 | 217 | 218 | #if (NGX_HAVE_INET6) 219 | 220 | static ngx_int_t 221 | ngx_rtmp_access_inet6(ngx_rtmp_session_t *s, u_char *p, ngx_uint_t flag) 222 | { 223 | ngx_uint_t n; 224 | ngx_uint_t i; 225 | ngx_rtmp_access_rule6_t *rule6; 226 | ngx_rtmp_access_app_conf_t *ascf; 227 | 228 | ascf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_access_module); 229 | 230 | rule6 = ascf->rules6.elts; 231 | for (i = 0; i < ascf->rules6.nelts; i++) { 232 | 233 | #if (NGX_DEBUG) 234 | { 235 | size_t cl, ml, al; 236 | u_char ct[NGX_INET6_ADDRSTRLEN]; 237 | u_char mt[NGX_INET6_ADDRSTRLEN]; 238 | u_char at[NGX_INET6_ADDRSTRLEN]; 239 | 240 | cl = ngx_inet6_ntop(p, ct, NGX_INET6_ADDRSTRLEN); 241 | ml = ngx_inet6_ntop(rule6[i].mask.s6_addr, mt, NGX_INET6_ADDRSTRLEN); 242 | al = ngx_inet6_ntop(rule6[i].addr.s6_addr, at, NGX_INET6_ADDRSTRLEN); 243 | 244 | ngx_log_debug6(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, 245 | "access: %*s %*s %*s", cl, ct, ml, mt, al, at); 246 | } 247 | #endif 248 | 249 | for (n = 0; n < 16; n++) { 250 | if ((p[n] & rule6[i].mask.s6_addr[n]) != rule6[i].addr.s6_addr[n]) { 251 | goto next; 252 | } 253 | } 254 | 255 | if (flag & rule6[i].flags) { 256 | return ngx_rtmp_access_found(s, rule6[i].deny); 257 | } 258 | 259 | next: 260 | continue; 261 | } 262 | 263 | return NGX_OK; 264 | } 265 | 266 | #endif 267 | 268 | 269 | static ngx_int_t 270 | ngx_rtmp_access(ngx_rtmp_session_t *s, ngx_uint_t flag) 271 | { 272 | struct sockaddr_in *sin; 273 | ngx_rtmp_access_app_conf_t *ascf; 274 | #if (NGX_HAVE_INET6) 275 | u_char *p; 276 | in_addr_t addr; 277 | struct sockaddr_in6 *sin6; 278 | #endif 279 | 280 | ascf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_access_module); 281 | if (ascf == NULL) { 282 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, s->connection->log, 0, 283 | "access: NULL app conf"); 284 | return NGX_ERROR; 285 | } 286 | 287 | /* relay etc */ 288 | if (s->connection->sockaddr == NULL) { 289 | return NGX_OK; 290 | } 291 | 292 | switch (s->connection->sockaddr->sa_family) { 293 | 294 | case AF_INET: 295 | sin = (struct sockaddr_in *) s->connection->sockaddr; 296 | return ngx_rtmp_access_inet(s, sin->sin_addr.s_addr, flag); 297 | 298 | #if (NGX_HAVE_INET6) 299 | 300 | case AF_INET6: 301 | sin6 = (struct sockaddr_in6 *) s->connection->sockaddr; 302 | p = sin6->sin6_addr.s6_addr; 303 | 304 | if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 305 | addr = p[12] << 24; 306 | addr += p[13] << 16; 307 | addr += p[14] << 8; 308 | addr += p[15]; 309 | return ngx_rtmp_access_inet(s, htonl(addr), flag); 310 | } 311 | 312 | return ngx_rtmp_access_inet6(s, p, flag); 313 | 314 | #endif 315 | } 316 | 317 | return NGX_OK; 318 | } 319 | 320 | 321 | static char * 322 | ngx_rtmp_access_rule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 323 | { 324 | ngx_rtmp_access_app_conf_t *ascf = conf; 325 | 326 | ngx_int_t rc; 327 | ngx_uint_t all; 328 | ngx_str_t *value; 329 | ngx_cidr_t cidr; 330 | ngx_rtmp_access_rule_t *rule; 331 | #if (NGX_HAVE_INET6) 332 | ngx_rtmp_access_rule6_t *rule6; 333 | #endif 334 | size_t n; 335 | ngx_uint_t flags; 336 | 337 | ngx_memzero(&cidr, sizeof(ngx_cidr_t)); 338 | 339 | value = cf->args->elts; 340 | 341 | n = 1; 342 | flags = 0; 343 | 344 | if (cf->args->nelts == 2) { 345 | 346 | flags = NGX_RTMP_ACCESS_PUBLISH | NGX_RTMP_ACCESS_PLAY; 347 | 348 | } else { 349 | 350 | for(; n < cf->args->nelts - 1; ++n) { 351 | 352 | if (value[n].len == sizeof("publish") - 1 && 353 | ngx_strcmp(value[1].data, "publish") == 0) 354 | { 355 | flags |= NGX_RTMP_ACCESS_PUBLISH; 356 | continue; 357 | 358 | } 359 | 360 | if (value[n].len == sizeof("play") - 1 && 361 | ngx_strcmp(value[1].data, "play") == 0) 362 | { 363 | flags |= NGX_RTMP_ACCESS_PLAY; 364 | continue; 365 | 366 | } 367 | 368 | ngx_log_error(NGX_LOG_ERR, cf->log, 0, 369 | "unexpected access specified: '%V'", &value[n]); 370 | return NGX_CONF_ERROR; 371 | } 372 | } 373 | 374 | all = (value[n].len == 3 && ngx_strcmp(value[n].data, "all") == 0); 375 | 376 | if (!all) { 377 | 378 | rc = ngx_ptocidr(&value[n], &cidr); 379 | 380 | if (rc == NGX_ERROR) { 381 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 382 | "invalid parameter \"%V\"", &value[1]); 383 | return NGX_CONF_ERROR; 384 | } 385 | 386 | if (rc == NGX_DONE) { 387 | ngx_conf_log_error(NGX_LOG_WARN, cf, 0, 388 | "low address bits of %V are meaningless", 389 | &value[1]); 390 | } 391 | } 392 | 393 | switch (cidr.family) { 394 | 395 | #if (NGX_HAVE_INET6) 396 | case AF_INET6: 397 | case 0: /* all */ 398 | 399 | rule6 = ngx_array_push(&ascf->rules6); 400 | if (rule6 == NULL) { 401 | return NGX_CONF_ERROR; 402 | } 403 | 404 | rule6->mask = cidr.u.in6.mask; 405 | rule6->addr = cidr.u.in6.addr; 406 | rule6->deny = (value[0].data[0] == 'd') ? 1 : 0; 407 | rule6->flags = flags; 408 | 409 | if (!all) { 410 | break; 411 | } 412 | /* "all" passes through */ 413 | #endif 414 | /* fall through */ 415 | 416 | default: /* AF_INET */ 417 | 418 | rule = ngx_array_push(&ascf->rules); 419 | if (rule == NULL) { 420 | return NGX_CONF_ERROR; 421 | } 422 | 423 | rule->mask = cidr.u.in.mask; 424 | rule->addr = cidr.u.in.addr; 425 | rule->deny = (value[0].data[0] == 'd') ? 1 : 0; 426 | rule->flags = flags; 427 | } 428 | 429 | return NGX_CONF_OK; 430 | } 431 | 432 | 433 | static ngx_int_t 434 | ngx_rtmp_access_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) 435 | { 436 | if (s->auto_pushed) { 437 | goto next; 438 | } 439 | 440 | if (ngx_rtmp_access(s, NGX_RTMP_ACCESS_PUBLISH) != NGX_OK) { 441 | return NGX_ERROR; 442 | } 443 | 444 | next: 445 | return next_publish(s, v); 446 | } 447 | 448 | 449 | static ngx_int_t 450 | ngx_rtmp_access_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v) 451 | { 452 | ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0, 453 | "access: ngx_rtmp_access_play"); 454 | 455 | if (ngx_rtmp_access(s, NGX_RTMP_ACCESS_PLAY) != NGX_OK) { 456 | ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0, 457 | "access: ngx_rtmp_access_play: error"); 458 | return NGX_ERROR; 459 | } 460 | 461 | ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0, 462 | "access: ngx_rtmp_access_play: next"); 463 | return next_play(s, v); 464 | } 465 | 466 | 467 | static ngx_int_t 468 | ngx_rtmp_access_postconfiguration(ngx_conf_t *cf) 469 | { 470 | /* chain handlers */ 471 | next_publish = ngx_rtmp_publish; 472 | ngx_rtmp_publish = ngx_rtmp_access_publish; 473 | 474 | next_play = ngx_rtmp_play; 475 | ngx_rtmp_play = ngx_rtmp_access_play; 476 | 477 | return NGX_OK; 478 | } 479 | -------------------------------------------------------------------------------- /ngx_rtmp_amf.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #ifndef _NGX_RTMP_AMF_H_INCLUDED_ 8 | #define _NGX_RTMP_AMF_H_INCLUDED_ 9 | 10 | 11 | #include 12 | #include 13 | 14 | 15 | /* basic types */ 16 | #define NGX_RTMP_AMF_NUMBER 0x00 17 | #define NGX_RTMP_AMF_BOOLEAN 0x01 18 | #define NGX_RTMP_AMF_STRING 0x02 19 | #define NGX_RTMP_AMF_OBJECT 0x03 20 | #define NGX_RTMP_AMF_NULL 0x05 21 | #define NGX_RTMP_AMF_ARRAY_NULL 0x06 22 | #define NGX_RTMP_AMF_MIXED_ARRAY 0x08 23 | #define NGX_RTMP_AMF_END 0x09 24 | #define NGX_RTMP_AMF_ARRAY 0x0a 25 | 26 | /* extended types */ 27 | #define NGX_RTMP_AMF_INT8 0x0100 28 | #define NGX_RTMP_AMF_INT16 0x0101 29 | #define NGX_RTMP_AMF_INT32 0x0102 30 | #define NGX_RTMP_AMF_VARIANT_ 0x0103 31 | 32 | /* r/w flags */ 33 | #define NGX_RTMP_AMF_OPTIONAL 0x1000 34 | #define NGX_RTMP_AMF_TYPELESS 0x2000 35 | #define NGX_RTMP_AMF_CONTEXT 0x4000 36 | 37 | #define NGX_RTMP_AMF_VARIANT (NGX_RTMP_AMF_VARIANT_\ 38 | |NGX_RTMP_AMF_TYPELESS) 39 | 40 | 41 | typedef struct { 42 | ngx_int_t type; 43 | ngx_str_t name; 44 | void *data; 45 | size_t len; 46 | } ngx_rtmp_amf_elt_t; 47 | 48 | 49 | typedef ngx_chain_t * (*ngx_rtmp_amf_alloc_pt)(void *arg); 50 | 51 | 52 | typedef struct { 53 | ngx_chain_t *link, *first; 54 | size_t offset; 55 | ngx_rtmp_amf_alloc_pt alloc; 56 | void *arg; 57 | ngx_log_t *log; 58 | } ngx_rtmp_amf_ctx_t; 59 | 60 | 61 | /* reading AMF */ 62 | ngx_int_t ngx_rtmp_amf_read(ngx_rtmp_amf_ctx_t *ctx, 63 | ngx_rtmp_amf_elt_t *elts, size_t nelts); 64 | 65 | /* writing AMF */ 66 | ngx_int_t ngx_rtmp_amf_write(ngx_rtmp_amf_ctx_t *ctx, 67 | ngx_rtmp_amf_elt_t *elts, size_t nelts); 68 | 69 | 70 | #endif /* _NGX_RTMP_AMF_H_INCLUDED_ */ 71 | 72 | -------------------------------------------------------------------------------- /ngx_rtmp_bandwidth.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include "ngx_rtmp_bandwidth.h" 10 | 11 | 12 | void 13 | ngx_rtmp_update_bandwidth(ngx_rtmp_bandwidth_t *bw, uint32_t bytes) 14 | { 15 | if (ngx_cached_time->sec > bw->intl_end) { 16 | bw->bandwidth = ngx_cached_time->sec > 17 | bw->intl_end + NGX_RTMP_BANDWIDTH_INTERVAL 18 | ? 0 19 | : bw->intl_bytes / NGX_RTMP_BANDWIDTH_INTERVAL; 20 | bw->intl_bytes = 0; 21 | bw->intl_end = ngx_cached_time->sec + NGX_RTMP_BANDWIDTH_INTERVAL; 22 | } 23 | 24 | bw->bytes += bytes; 25 | bw->intl_bytes += bytes; 26 | } 27 | -------------------------------------------------------------------------------- /ngx_rtmp_bandwidth.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #ifndef _NGX_RTMP_BANDWIDTH_H_INCLUDED_ 8 | #define _NGX_RTMP_BANDWIDTH_H_INCLUDED_ 9 | 10 | 11 | #include 12 | #include 13 | 14 | 15 | /* Bandwidth update interval in seconds */ 16 | #define NGX_RTMP_BANDWIDTH_INTERVAL 10 17 | 18 | 19 | typedef struct { 20 | uint64_t bytes; 21 | uint64_t bandwidth; /* bytes/sec */ 22 | 23 | time_t intl_end; 24 | uint64_t intl_bytes; 25 | } ngx_rtmp_bandwidth_t; 26 | 27 | 28 | void ngx_rtmp_update_bandwidth(ngx_rtmp_bandwidth_t *bw, uint32_t bytes); 29 | 30 | 31 | #endif /* _NGX_RTMP_BANDWIDTH_H_INCLUDED_ */ 32 | -------------------------------------------------------------------------------- /ngx_rtmp_bitop.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include "ngx_rtmp_bitop.h" 10 | 11 | 12 | void 13 | ngx_rtmp_bit_init_reader(ngx_rtmp_bit_reader_t *br, u_char *pos, u_char *last) 14 | { 15 | ngx_memzero(br, sizeof(ngx_rtmp_bit_reader_t)); 16 | 17 | br->pos = pos; 18 | br->last = last; 19 | } 20 | 21 | 22 | uint64_t 23 | ngx_rtmp_bit_read(ngx_rtmp_bit_reader_t *br, ngx_uint_t n) 24 | { 25 | uint64_t v; 26 | ngx_uint_t d; 27 | 28 | v = 0; 29 | 30 | while (n) { 31 | 32 | if (br->pos >= br->last) { 33 | br->err = 1; 34 | return 0; 35 | } 36 | 37 | d = (br->offs + n > 8 ? (ngx_uint_t) (8 - br->offs) : n); 38 | 39 | v <<= d; 40 | v += (*br->pos >> (8 - br->offs - d)) & ((u_char) 0xff >> (8 - d)); 41 | 42 | br->offs += d; 43 | n -= d; 44 | 45 | if (br->offs == 8) { 46 | br->pos++; 47 | br->offs = 0; 48 | } 49 | } 50 | 51 | return v; 52 | } 53 | 54 | 55 | uint64_t 56 | ngx_rtmp_bit_read_golomb(ngx_rtmp_bit_reader_t *br) 57 | { 58 | ngx_uint_t n; 59 | 60 | for (n = 0; ngx_rtmp_bit_read(br, 1) == 0 && !br->err; n++); 61 | 62 | return ((uint64_t) 1 << n) + ngx_rtmp_bit_read(br, n) - 1; 63 | } 64 | -------------------------------------------------------------------------------- /ngx_rtmp_bitop.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #ifndef _NGX_RTMP_BITOP_H_INCLUDED_ 8 | #define _NGX_RTMP_BITOP_H_INCLUDED_ 9 | 10 | 11 | #include 12 | #include 13 | 14 | 15 | typedef struct { 16 | u_char *pos; 17 | u_char *last; 18 | ngx_uint_t offs; 19 | ngx_uint_t err; 20 | } ngx_rtmp_bit_reader_t; 21 | 22 | 23 | void ngx_rtmp_bit_init_reader(ngx_rtmp_bit_reader_t *br, u_char *pos, 24 | u_char *last); 25 | uint64_t ngx_rtmp_bit_read(ngx_rtmp_bit_reader_t *br, ngx_uint_t n); 26 | uint64_t ngx_rtmp_bit_read_golomb(ngx_rtmp_bit_reader_t *br); 27 | 28 | 29 | #define ngx_rtmp_bit_read_err(br) ((br)->err) 30 | 31 | #define ngx_rtmp_bit_read_eof(br) ((br)->pos == (br)->last) 32 | 33 | #define ngx_rtmp_bit_read_8(br) \ 34 | ((uint8_t) ngx_rtmp_bit_read(br, 8)) 35 | 36 | #define ngx_rtmp_bit_read_16(br) \ 37 | ((uint16_t) ngx_rtmp_bit_read(br, 16)) 38 | 39 | #define ngx_rtmp_bit_read_32(br) \ 40 | ((uint32_t) ngx_rtmp_bit_read(br, 32)) 41 | 42 | #define ngx_rtmp_bit_read_64(br) \ 43 | ((uint64_t) ngx_rtmp_read(br, 64)) 44 | 45 | 46 | #endif /* _NGX_RTMP_BITOP_H_INCLUDED_ */ 47 | -------------------------------------------------------------------------------- /ngx_rtmp_cmd_module.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #ifndef _NGX_RTMP_CMD_H_INCLUDED_ 8 | #define _NGX_RTMP_CMD_H_INCLUDED_ 9 | 10 | 11 | #include 12 | #include 13 | #include 14 | #include "ngx_rtmp.h" 15 | 16 | 17 | #define NGX_RTMP_MAX_NAME 2048 18 | #define NGX_RTMP_MAX_URL 4096 19 | #define NGX_RTMP_MAX_ARGS NGX_RTMP_MAX_NAME 20 | 21 | 22 | /* Basic RTMP call support */ 23 | 24 | typedef struct { 25 | double trans; 26 | u_char app[NGX_RTMP_MAX_NAME]; 27 | u_char args[NGX_RTMP_MAX_ARGS]; 28 | u_char flashver[32]; 29 | u_char swf_url[NGX_RTMP_MAX_URL]; 30 | u_char tc_url[NGX_RTMP_MAX_URL]; 31 | double acodecs; 32 | double vcodecs; 33 | u_char page_url[NGX_RTMP_MAX_URL]; 34 | double object_encoding; 35 | } ngx_rtmp_connect_t; 36 | 37 | 38 | typedef struct { 39 | double trans; 40 | double stream; 41 | } ngx_rtmp_create_stream_t; 42 | 43 | 44 | typedef struct { 45 | double stream; 46 | } ngx_rtmp_delete_stream_t; 47 | 48 | 49 | typedef struct { 50 | double stream; 51 | } ngx_rtmp_close_stream_t; 52 | 53 | 54 | typedef struct { 55 | u_char name[NGX_RTMP_MAX_NAME]; 56 | u_char args[NGX_RTMP_MAX_ARGS]; 57 | u_char type[16]; 58 | int silent; 59 | } ngx_rtmp_publish_t; 60 | 61 | 62 | typedef struct { 63 | ngx_str_t playlist; 64 | ngx_str_t module; 65 | } ngx_rtmp_playlist_t; 66 | 67 | 68 | typedef struct { 69 | u_char name[NGX_RTMP_MAX_NAME]; 70 | u_char args[NGX_RTMP_MAX_ARGS]; 71 | double start; 72 | double duration; 73 | int reset; 74 | int silent; 75 | } ngx_rtmp_play_t; 76 | 77 | 78 | typedef struct { 79 | double offset; 80 | } ngx_rtmp_seek_t; 81 | 82 | 83 | typedef struct { 84 | uint8_t pause; 85 | double position; 86 | } ngx_rtmp_pause_t; 87 | 88 | 89 | typedef struct { 90 | uint32_t msid; 91 | } ngx_rtmp_msid_t; 92 | 93 | 94 | typedef ngx_rtmp_msid_t ngx_rtmp_stream_begin_t; 95 | typedef ngx_rtmp_msid_t ngx_rtmp_stream_eof_t; 96 | typedef ngx_rtmp_msid_t ngx_rtmp_stream_dry_t; 97 | typedef ngx_rtmp_msid_t ngx_rtmp_recorded_t; 98 | 99 | 100 | typedef struct { 101 | uint32_t msid; 102 | uint32_t buflen; 103 | } ngx_rtmp_set_buflen_t; 104 | 105 | 106 | void ngx_rtmp_cmd_fill_args(u_char name[NGX_RTMP_MAX_NAME], 107 | u_char args[NGX_RTMP_MAX_ARGS]); 108 | 109 | 110 | typedef ngx_int_t (*ngx_rtmp_connect_pt)(ngx_rtmp_session_t *s, 111 | ngx_rtmp_connect_t *v); 112 | typedef ngx_int_t (*ngx_rtmp_disconnect_pt)(ngx_rtmp_session_t *s); 113 | typedef ngx_int_t (*ngx_rtmp_create_stream_pt)(ngx_rtmp_session_t *s, 114 | ngx_rtmp_create_stream_t *v); 115 | typedef ngx_int_t (*ngx_rtmp_close_stream_pt)(ngx_rtmp_session_t *s, 116 | ngx_rtmp_close_stream_t *v); 117 | typedef ngx_int_t (*ngx_rtmp_delete_stream_pt)(ngx_rtmp_session_t *s, 118 | ngx_rtmp_delete_stream_t *v); 119 | typedef ngx_int_t (*ngx_rtmp_publish_pt)(ngx_rtmp_session_t *s, 120 | ngx_rtmp_publish_t *v); 121 | typedef ngx_int_t (*ngx_rtmp_play_pt)(ngx_rtmp_session_t *s, 122 | ngx_rtmp_play_t *v); 123 | typedef ngx_int_t (*ngx_rtmp_seek_pt)(ngx_rtmp_session_t *s, 124 | ngx_rtmp_seek_t *v); 125 | typedef ngx_int_t (*ngx_rtmp_pause_pt)(ngx_rtmp_session_t *s, 126 | ngx_rtmp_pause_t *v); 127 | 128 | typedef ngx_int_t (*ngx_rtmp_stream_begin_pt)(ngx_rtmp_session_t *s, 129 | ngx_rtmp_stream_begin_t *v); 130 | typedef ngx_int_t (*ngx_rtmp_stream_eof_pt)(ngx_rtmp_session_t *s, 131 | ngx_rtmp_stream_eof_t *v); 132 | typedef ngx_int_t (*ngx_rtmp_stream_dry_pt)(ngx_rtmp_session_t *s, 133 | ngx_rtmp_stream_dry_t *v); 134 | typedef ngx_int_t (*ngx_rtmp_recorded_pt)(ngx_rtmp_session_t *s, 135 | ngx_rtmp_recorded_t *v); 136 | typedef ngx_int_t (*ngx_rtmp_set_buflen_pt)(ngx_rtmp_session_t *s, 137 | ngx_rtmp_set_buflen_t *v); 138 | 139 | typedef ngx_int_t (*ngx_rtmp_playlist_pt)(ngx_rtmp_session_t *s, ngx_rtmp_playlist_t *v); 140 | 141 | extern ngx_rtmp_connect_pt ngx_rtmp_connect; 142 | extern ngx_rtmp_disconnect_pt ngx_rtmp_disconnect; 143 | extern ngx_rtmp_create_stream_pt ngx_rtmp_create_stream; 144 | extern ngx_rtmp_close_stream_pt ngx_rtmp_close_stream; 145 | extern ngx_rtmp_delete_stream_pt ngx_rtmp_delete_stream; 146 | extern ngx_rtmp_publish_pt ngx_rtmp_publish; 147 | extern ngx_rtmp_play_pt ngx_rtmp_play; 148 | extern ngx_rtmp_seek_pt ngx_rtmp_seek; 149 | extern ngx_rtmp_pause_pt ngx_rtmp_pause; 150 | 151 | extern ngx_rtmp_stream_begin_pt ngx_rtmp_stream_begin; 152 | extern ngx_rtmp_stream_eof_pt ngx_rtmp_stream_eof; 153 | extern ngx_rtmp_stream_dry_pt ngx_rtmp_stream_dry; 154 | extern ngx_rtmp_set_buflen_pt ngx_rtmp_set_buflen; 155 | extern ngx_rtmp_recorded_pt ngx_rtmp_recorded; 156 | 157 | extern ngx_rtmp_playlist_pt ngx_rtmp_playlist; 158 | 159 | #endif /*_NGX_RTMP_CMD_H_INCLUDED_ */ 160 | -------------------------------------------------------------------------------- /ngx_rtmp_codec_module.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #ifndef _NGX_RTMP_CODEC_H_INCLUDED_ 8 | #define _NGX_RTMP_CODEC_H_INCLUDED_ 9 | 10 | 11 | #include 12 | #include 13 | #include "ngx_rtmp.h" 14 | 15 | 16 | /* Audio codecs */ 17 | enum { 18 | /* Uncompressed codec id is actually 0, 19 | * but we use another value for consistency */ 20 | NGX_RTMP_AUDIO_UNCOMPRESSED = 16, 21 | NGX_RTMP_AUDIO_ADPCM = 1, 22 | NGX_RTMP_AUDIO_MP3 = 2, 23 | NGX_RTMP_AUDIO_LINEAR_LE = 3, 24 | NGX_RTMP_AUDIO_NELLY16 = 4, 25 | NGX_RTMP_AUDIO_NELLY8 = 5, 26 | NGX_RTMP_AUDIO_NELLY = 6, 27 | NGX_RTMP_AUDIO_G711A = 7, 28 | NGX_RTMP_AUDIO_G711U = 8, 29 | NGX_RTMP_AUDIO_AAC = 10, 30 | NGX_RTMP_AUDIO_SPEEX = 11, 31 | NGX_RTMP_AUDIO_MP3_8 = 14, 32 | NGX_RTMP_AUDIO_DEVSPEC = 15, 33 | }; 34 | 35 | 36 | /* Video codecs */ 37 | enum { 38 | NGX_RTMP_VIDEO_JPEG = 1, 39 | NGX_RTMP_VIDEO_SORENSON_H263 = 2, 40 | NGX_RTMP_VIDEO_SCREEN = 3, 41 | NGX_RTMP_VIDEO_ON2_VP6 = 4, 42 | NGX_RTMP_VIDEO_ON2_VP6_ALPHA = 5, 43 | NGX_RTMP_VIDEO_SCREEN2 = 6, 44 | NGX_RTMP_VIDEO_H264 = 7 45 | }; 46 | 47 | 48 | u_char * ngx_rtmp_get_audio_codec_name(ngx_uint_t id); 49 | u_char * ngx_rtmp_get_video_codec_name(ngx_uint_t id); 50 | 51 | 52 | typedef struct { 53 | ngx_uint_t width; 54 | ngx_uint_t height; 55 | double duration; 56 | double frame_rate; 57 | double video_data_rate; 58 | ngx_uint_t video_codec_id; 59 | double audio_data_rate; 60 | ngx_uint_t audio_codec_id; 61 | ngx_uint_t aac_profile; 62 | ngx_uint_t aac_chan_conf; 63 | ngx_uint_t aac_sbr; 64 | ngx_uint_t aac_ps; 65 | ngx_uint_t avc_profile; 66 | ngx_uint_t avc_compat; 67 | ngx_uint_t avc_level; 68 | ngx_uint_t avc_nal_bytes; 69 | ngx_uint_t avc_ref_frames; 70 | ngx_uint_t sample_rate; /* 5512, 11025, 22050, 44100 */ 71 | ngx_uint_t sample_size; /* 1=8bit, 2=16bit */ 72 | ngx_uint_t audio_channels; /* 1, 2 */ 73 | u_char profile[32]; 74 | u_char level[32]; 75 | 76 | ngx_chain_t *avc_header; 77 | ngx_chain_t *aac_header; 78 | 79 | ngx_chain_t *meta; 80 | ngx_uint_t meta_version; 81 | } ngx_rtmp_codec_ctx_t; 82 | 83 | 84 | extern ngx_module_t ngx_rtmp_codec_module; 85 | 86 | 87 | #endif /* _NGX_RTMP_LIVE_H_INCLUDED_ */ 88 | -------------------------------------------------------------------------------- /ngx_rtmp_eval.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include "ngx_rtmp_eval.h" 10 | 11 | 12 | #define NGX_RTMP_EVAL_BUFLEN 16 13 | 14 | 15 | static void 16 | ngx_rtmp_eval_session_str(void *ctx, ngx_rtmp_eval_t *e, ngx_str_t *ret) 17 | { 18 | *ret = *(ngx_str_t *) ((u_char *) ctx + e->offset); 19 | } 20 | 21 | 22 | static void 23 | ngx_rtmp_eval_connection_str(void *ctx, ngx_rtmp_eval_t *e, ngx_str_t *ret) 24 | { 25 | ngx_rtmp_session_t *s = ctx; 26 | 27 | *ret = *(ngx_str_t *) ((u_char *) s->connection + e->offset); 28 | } 29 | 30 | 31 | ngx_rtmp_eval_t ngx_rtmp_eval_session[] = { 32 | 33 | { ngx_string("app"), 34 | ngx_rtmp_eval_session_str, 35 | offsetof(ngx_rtmp_session_t, app) }, 36 | 37 | { ngx_string("flashver"), 38 | ngx_rtmp_eval_session_str, 39 | offsetof(ngx_rtmp_session_t, flashver) }, 40 | 41 | { ngx_string("swfurl"), 42 | ngx_rtmp_eval_session_str, 43 | offsetof(ngx_rtmp_session_t, swf_url) }, 44 | 45 | { ngx_string("tcurl"), 46 | ngx_rtmp_eval_session_str, 47 | offsetof(ngx_rtmp_session_t, tc_url) }, 48 | 49 | { ngx_string("pageurl"), 50 | ngx_rtmp_eval_session_str, 51 | offsetof(ngx_rtmp_session_t, page_url) }, 52 | 53 | { ngx_string("addr"), 54 | ngx_rtmp_eval_connection_str, 55 | offsetof(ngx_connection_t, addr_text) }, 56 | 57 | ngx_rtmp_null_eval 58 | }; 59 | 60 | 61 | static void 62 | ngx_rtmp_eval_append(ngx_buf_t *b, void *data, size_t len, ngx_log_t *log) 63 | { 64 | size_t buf_len; 65 | 66 | if (b->last + len > b->end) { 67 | buf_len = 2 * (b->last - b->pos) + len; 68 | 69 | b->start = ngx_alloc(buf_len, log); 70 | if (b->start == NULL) { 71 | return; 72 | } 73 | 74 | b->last = ngx_cpymem(b->start, b->pos, b->last - b->pos); 75 | b->pos = b->start; 76 | b->end = b->start + buf_len; 77 | } 78 | 79 | b->last = ngx_cpymem(b->last, data, len); 80 | } 81 | 82 | 83 | static void 84 | ngx_rtmp_eval_append_var(void *ctx, ngx_buf_t *b, ngx_rtmp_eval_t **e, 85 | ngx_str_t *name, ngx_log_t *log) 86 | { 87 | ngx_uint_t k; 88 | ngx_str_t v; 89 | ngx_rtmp_eval_t *ee; 90 | 91 | for (; *e; ++e) { 92 | for (k = 0, ee = *e; ee->handler; ++k, ++ee) { 93 | if (ee->name.len == name->len && 94 | ngx_memcmp(ee->name.data, name->data, name->len) == 0) 95 | { 96 | ee->handler(ctx, ee, &v); 97 | ngx_rtmp_eval_append(b, v.data, v.len, log); 98 | } 99 | } 100 | } 101 | } 102 | 103 | 104 | ngx_int_t 105 | ngx_rtmp_eval(void *ctx, ngx_str_t *in, ngx_rtmp_eval_t **e, ngx_str_t *out, 106 | ngx_log_t *log) 107 | { 108 | u_char c, *p; 109 | ngx_str_t name; 110 | ngx_buf_t b; 111 | ngx_uint_t n; 112 | 113 | enum { 114 | NORMAL, 115 | ESCAPE, 116 | NAME, 117 | SNAME 118 | } state = NORMAL; 119 | 120 | b.pos = b.last = b.start = ngx_alloc(NGX_RTMP_EVAL_BUFLEN, log); 121 | if (b.pos == NULL) { 122 | return NGX_ERROR; 123 | } 124 | 125 | b.end = b.pos + NGX_RTMP_EVAL_BUFLEN; 126 | name.data = NULL; 127 | 128 | for (n = 0; n < in->len; ++n) { 129 | p = &in->data[n]; 130 | c = *p; 131 | 132 | switch (state) { 133 | case SNAME: 134 | if (c != '}') { 135 | continue; 136 | } 137 | 138 | name.len = p - name.data; 139 | ngx_rtmp_eval_append_var(ctx, &b, e, &name, log); 140 | 141 | state = NORMAL; 142 | 143 | continue; 144 | 145 | case NAME: 146 | if (c == '{' && name.data == p) { 147 | ++name.data; 148 | state = SNAME; 149 | continue; 150 | } 151 | if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { 152 | continue; 153 | } 154 | 155 | name.len = p - name.data; 156 | ngx_rtmp_eval_append_var(ctx, &b, e, &name, log); 157 | /* fall through */ 158 | 159 | case NORMAL: 160 | switch (c) { 161 | case '$': 162 | name.data = p + 1; 163 | state = NAME; 164 | continue; 165 | case '\\': 166 | state = ESCAPE; 167 | continue; 168 | } 169 | 170 | case ESCAPE: 171 | ngx_rtmp_eval_append(&b, &c, 1, log); 172 | state = NORMAL; 173 | break; 174 | 175 | } 176 | } 177 | 178 | if (state == NAME) { 179 | p = &in->data[n]; 180 | name.len = p - name.data; 181 | ngx_rtmp_eval_append_var(ctx, &b, e, &name, log); 182 | } 183 | 184 | c = 0; 185 | ngx_rtmp_eval_append(&b, &c, 1, log); 186 | 187 | out->data = b.pos; 188 | out->len = b.last - b.pos - 1; 189 | 190 | return NGX_OK; 191 | } 192 | 193 | 194 | ngx_int_t 195 | ngx_rtmp_eval_streams(ngx_str_t *in) 196 | { 197 | #if !(NGX_WIN32) 198 | ngx_int_t mode, create, v, close_src; 199 | ngx_fd_t dst, src; 200 | u_char *path; 201 | 202 | path = in->data; 203 | 204 | while (*path >= '0' && *path <= '9') { 205 | path++; 206 | } 207 | 208 | switch ((char) *path) { 209 | 210 | case '>': 211 | 212 | v = (path == in->data ? 1 : ngx_atoi(in->data, path - in->data)); 213 | if (v == NGX_ERROR) { 214 | return NGX_ERROR; 215 | } 216 | 217 | dst = (ngx_fd_t) v; 218 | mode = NGX_FILE_WRONLY; 219 | create = NGX_FILE_TRUNCATE; 220 | path++; 221 | 222 | if (*path == (u_char) '>') { 223 | mode = NGX_FILE_APPEND; 224 | create = NGX_FILE_CREATE_OR_OPEN; 225 | path++; 226 | } 227 | 228 | break; 229 | 230 | case '<': 231 | 232 | v = (path == in->data ? 0 : ngx_atoi(in->data, path - in->data)); 233 | if (v == NGX_ERROR) { 234 | return NGX_ERROR; 235 | } 236 | 237 | dst = (ngx_fd_t) v; 238 | mode = NGX_FILE_RDONLY; 239 | create = NGX_FILE_OPEN; 240 | path++; 241 | 242 | break; 243 | 244 | default: 245 | 246 | return NGX_DONE; 247 | } 248 | 249 | if (*path == (u_char) '&') { 250 | 251 | path++; 252 | v = ngx_atoi(path, in->data + in->len - path); 253 | if (v == NGX_ERROR) { 254 | return NGX_ERROR; 255 | } 256 | src = (ngx_fd_t) v; 257 | close_src = 0; 258 | 259 | } else { 260 | 261 | src = ngx_open_file(path, mode, create, NGX_FILE_DEFAULT_ACCESS); 262 | if (src == NGX_INVALID_FILE) { 263 | return NGX_ERROR; 264 | } 265 | close_src = 1; 266 | 267 | } 268 | 269 | if (src == dst) { 270 | return NGX_OK; 271 | } 272 | 273 | dup2(src, dst); 274 | 275 | if (close_src) { 276 | ngx_close_file(src); 277 | } 278 | return NGX_OK; 279 | 280 | #else 281 | return NGX_DONE; 282 | #endif 283 | } 284 | -------------------------------------------------------------------------------- /ngx_rtmp_eval.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #ifndef _NGX_RTMP_EVAL_H_INCLUDED_ 8 | #define _NGX_RTMP_EVAL_H_INCLUDED_ 9 | 10 | 11 | #include 12 | #include 13 | #include "ngx_rtmp.h" 14 | 15 | 16 | typedef struct ngx_rtmp_eval_s ngx_rtmp_eval_t; 17 | 18 | 19 | typedef void (* ngx_rtmp_eval_pt)(void *ctx, ngx_rtmp_eval_t *e, 20 | ngx_str_t *ret); 21 | 22 | 23 | struct ngx_rtmp_eval_s { 24 | ngx_str_t name; 25 | ngx_rtmp_eval_pt handler; 26 | ngx_uint_t offset; 27 | }; 28 | 29 | 30 | #define ngx_rtmp_null_eval { ngx_null_string, NULL, 0 } 31 | 32 | 33 | /* standard session eval variables */ 34 | extern ngx_rtmp_eval_t ngx_rtmp_eval_session[]; 35 | 36 | 37 | ngx_int_t ngx_rtmp_eval(void *ctx, ngx_str_t *in, ngx_rtmp_eval_t **e, 38 | ngx_str_t *out, ngx_log_t *log); 39 | 40 | 41 | ngx_int_t ngx_rtmp_eval_streams(ngx_str_t *in); 42 | 43 | 44 | #endif /* _NGX_RTMP_EVAL_H_INCLUDED_ */ 45 | -------------------------------------------------------------------------------- /ngx_rtmp_init.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include "ngx_rtmp.h" 10 | #include "ngx_rtmp_proxy_protocol.h" 11 | 12 | 13 | static void ngx_rtmp_close_connection(ngx_connection_t *c); 14 | static u_char * ngx_rtmp_log_error(ngx_log_t *log, u_char *buf, size_t len); 15 | 16 | 17 | void 18 | ngx_rtmp_init_connection(ngx_connection_t *c) 19 | { 20 | ngx_uint_t i; 21 | ngx_rtmp_port_t *port; 22 | struct sockaddr *sa; 23 | struct sockaddr_in *sin; 24 | ngx_rtmp_in_addr_t *addr; 25 | ngx_rtmp_session_t *s; 26 | ngx_rtmp_addr_conf_t *addr_conf; 27 | ngx_int_t unix_socket; 28 | #if (NGX_HAVE_INET6) 29 | struct sockaddr_in6 *sin6; 30 | ngx_rtmp_in6_addr_t *addr6; 31 | #endif 32 | 33 | ++ngx_rtmp_naccepted; 34 | 35 | /* find the server configuration for the address:port */ 36 | 37 | /* AF_INET only */ 38 | 39 | port = c->listening->servers; 40 | unix_socket = 0; 41 | 42 | if (port->naddrs > 1) { 43 | 44 | /* 45 | * There are several addresses on this port and one of them 46 | * is the "*:port" wildcard so getsockname() is needed to determine 47 | * the server address. 48 | * 49 | * AcceptEx() already gave this address. 50 | */ 51 | 52 | if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { 53 | ngx_rtmp_close_connection(c); 54 | return; 55 | } 56 | 57 | sa = c->local_sockaddr; 58 | 59 | switch (sa->sa_family) { 60 | 61 | #if (NGX_HAVE_INET6) 62 | case AF_INET6: 63 | sin6 = (struct sockaddr_in6 *) sa; 64 | 65 | addr6 = port->addrs; 66 | 67 | /* the last address is "*" */ 68 | 69 | for (i = 0; i < port->naddrs - 1; i++) { 70 | if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) { 71 | break; 72 | } 73 | } 74 | 75 | addr_conf = &addr6[i].conf; 76 | 77 | break; 78 | #endif 79 | case AF_UNIX: 80 | unix_socket = 1; 81 | /* fall through */ 82 | 83 | default: /* AF_INET */ 84 | sin = (struct sockaddr_in *) sa; 85 | 86 | addr = port->addrs; 87 | 88 | /* the last address is "*" */ 89 | 90 | for (i = 0; i < port->naddrs - 1; i++) { 91 | if (addr[i].addr == sin->sin_addr.s_addr) { 92 | break; 93 | } 94 | } 95 | 96 | addr_conf = &addr[i].conf; 97 | 98 | break; 99 | } 100 | 101 | } else { 102 | switch (c->local_sockaddr->sa_family) { 103 | 104 | #if (NGX_HAVE_INET6) 105 | case AF_INET6: 106 | addr6 = port->addrs; 107 | addr_conf = &addr6[0].conf; 108 | break; 109 | #endif 110 | 111 | case AF_UNIX: 112 | unix_socket = 1; 113 | /* fall through */ 114 | 115 | default: /* AF_INET */ 116 | addr = port->addrs; 117 | addr_conf = &addr[0].conf; 118 | break; 119 | } 120 | } 121 | 122 | ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%ui client connected '%V'", 123 | c->number, &c->addr_text); 124 | 125 | s = ngx_rtmp_init_session(c, addr_conf); 126 | if (s == NULL) { 127 | return; 128 | } 129 | 130 | /* only auto-pushed connections are 131 | * done through unix socket */ 132 | 133 | s->auto_pushed = unix_socket; 134 | 135 | if (addr_conf->proxy_protocol) { 136 | ngx_rtmp_proxy_protocol(s); 137 | 138 | } else { 139 | ngx_rtmp_handshake(s); 140 | } 141 | } 142 | 143 | 144 | ngx_rtmp_session_t * 145 | ngx_rtmp_init_session(ngx_connection_t *c, ngx_rtmp_addr_conf_t *addr_conf) 146 | { 147 | ngx_rtmp_session_t *s; 148 | ngx_rtmp_core_srv_conf_t *cscf; 149 | ngx_rtmp_error_log_ctx_t *ctx; 150 | 151 | s = ngx_pcalloc(c->pool, sizeof(ngx_rtmp_session_t) + 152 | sizeof(ngx_chain_t *) * ((ngx_rtmp_core_srv_conf_t *) 153 | addr_conf->ctx-> srv_conf[ngx_rtmp_core_module 154 | .ctx_index])->out_queue); 155 | if (s == NULL) { 156 | ngx_rtmp_close_connection(c); 157 | return NULL; 158 | } 159 | 160 | s->main_conf = addr_conf->ctx->main_conf; 161 | s->srv_conf = addr_conf->ctx->srv_conf; 162 | 163 | s->addr_text = &addr_conf->addr_text; 164 | 165 | c->data = s; 166 | s->connection = c; 167 | 168 | ctx = ngx_palloc(c->pool, sizeof(ngx_rtmp_error_log_ctx_t)); 169 | if (ctx == NULL) { 170 | ngx_rtmp_close_connection(c); 171 | return NULL; 172 | } 173 | 174 | ctx->client = &c->addr_text; 175 | ctx->session = s; 176 | 177 | c->log->connection = c->number; 178 | c->log->handler = ngx_rtmp_log_error; 179 | c->log->data = ctx; 180 | c->log->action = NULL; 181 | 182 | c->log_error = NGX_ERROR_INFO; 183 | 184 | s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_rtmp_max_module); 185 | if (s->ctx == NULL) { 186 | ngx_rtmp_close_connection(c); 187 | return NULL; 188 | } 189 | 190 | cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module); 191 | 192 | s->out_queue = cscf->out_queue; 193 | s->out_cork = cscf->out_cork; 194 | s->in_streams = ngx_pcalloc(c->pool, sizeof(ngx_rtmp_stream_t) 195 | * cscf->max_streams); 196 | if (s->in_streams == NULL) { 197 | ngx_rtmp_close_connection(c); 198 | return NULL; 199 | } 200 | 201 | #if (nginx_version >= 1007005) 202 | ngx_queue_init(&s->posted_dry_events); 203 | #endif 204 | 205 | s->epoch = ngx_current_msec; 206 | s->timeout = cscf->timeout; 207 | s->buflen = cscf->buflen; 208 | ngx_rtmp_set_chunk_size(s, NGX_RTMP_DEFAULT_CHUNK_SIZE); 209 | 210 | 211 | if (ngx_rtmp_fire_event(s, NGX_RTMP_CONNECT, NULL, NULL) != NGX_OK) { 212 | ngx_rtmp_finalize_session(s); 213 | return NULL; 214 | } 215 | 216 | return s; 217 | } 218 | 219 | 220 | static u_char * 221 | ngx_rtmp_log_error(ngx_log_t *log, u_char *buf, size_t len) 222 | { 223 | u_char *p; 224 | ngx_rtmp_session_t *s; 225 | ngx_rtmp_error_log_ctx_t *ctx; 226 | 227 | if (log->action) { 228 | p = ngx_snprintf(buf, len, " while %s", log->action); 229 | len -= p - buf; 230 | buf = p; 231 | } 232 | 233 | ctx = log->data; 234 | 235 | p = ngx_snprintf(buf, len, ", client: %V", ctx->client); 236 | len -= p - buf; 237 | buf = p; 238 | 239 | s = ctx->session; 240 | 241 | if (s == NULL) { 242 | return p; 243 | } 244 | 245 | p = ngx_snprintf(buf, len, ", server: %V", s->addr_text); 246 | len -= p - buf; 247 | buf = p; 248 | 249 | return p; 250 | } 251 | 252 | 253 | static void 254 | ngx_rtmp_close_connection(ngx_connection_t *c) 255 | { 256 | ngx_pool_t *pool; 257 | 258 | ngx_log_debug0(NGX_LOG_DEBUG_RTMP, c->log, 0, "close connection"); 259 | 260 | #if (NGX_STAT_STUB) 261 | (void) ngx_atomic_fetch_add(ngx_stat_active, -1); 262 | #endif 263 | 264 | pool = c->pool; 265 | ngx_close_connection(c); 266 | ngx_destroy_pool(pool); 267 | } 268 | 269 | 270 | static void 271 | ngx_rtmp_close_session_handler(ngx_event_t *e) 272 | { 273 | ngx_rtmp_session_t *s; 274 | ngx_connection_t *c; 275 | ngx_rtmp_core_srv_conf_t *cscf; 276 | 277 | s = e->data; 278 | c = s->connection; 279 | 280 | cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module); 281 | 282 | ngx_log_debug0(NGX_LOG_DEBUG_RTMP, c->log, 0, "close session"); 283 | 284 | ngx_rtmp_fire_event(s, NGX_RTMP_DISCONNECT, NULL, NULL); 285 | 286 | if (s->ping_evt.timer_set) { 287 | ngx_del_timer(&s->ping_evt); 288 | } 289 | 290 | if (s->in_old_pool) { 291 | ngx_destroy_pool(s->in_old_pool); 292 | } 293 | 294 | if (s->in_pool) { 295 | ngx_destroy_pool(s->in_pool); 296 | } 297 | 298 | ngx_rtmp_free_handshake_buffers(s); 299 | 300 | while (s->out_pos != s->out_last) { 301 | ngx_rtmp_free_shared_chain(cscf, s->out[s->out_pos++]); 302 | s->out_pos %= s->out_queue; 303 | } 304 | 305 | ngx_rtmp_close_connection(c); 306 | } 307 | 308 | 309 | void 310 | ngx_rtmp_finalize_session(ngx_rtmp_session_t *s) 311 | { 312 | ngx_event_t *e; 313 | ngx_connection_t *c; 314 | 315 | c = s->connection; 316 | if (c->destroyed) { 317 | return; 318 | } 319 | 320 | ngx_log_debug0(NGX_LOG_DEBUG_RTMP, c->log, 0, "finalize session"); 321 | 322 | c->destroyed = 1; 323 | e = &s->close; 324 | e->data = s; 325 | e->handler = ngx_rtmp_close_session_handler; 326 | e->log = c->log; 327 | 328 | ngx_post_event(e, &ngx_posted_events); 329 | } 330 | 331 | -------------------------------------------------------------------------------- /ngx_rtmp_limit_module.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include "ngx_rtmp.h" 10 | 11 | 12 | typedef struct { 13 | ngx_int_t max_conn; 14 | ngx_shm_zone_t *shm_zone; 15 | } ngx_rtmp_limit_main_conf_t; 16 | 17 | 18 | static ngx_str_t shm_name = ngx_string("rtmp_limit"); 19 | 20 | 21 | static ngx_int_t ngx_rtmp_limit_postconfiguration(ngx_conf_t *cf); 22 | static void *ngx_rtmp_limit_create_main_conf(ngx_conf_t *cf); 23 | 24 | 25 | static ngx_command_t ngx_rtmp_limit_commands[] = { 26 | 27 | { ngx_string("max_connections"), 28 | NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1, 29 | ngx_conf_set_num_slot, 30 | NGX_RTMP_MAIN_CONF_OFFSET, 31 | offsetof(ngx_rtmp_limit_main_conf_t, max_conn), 32 | NULL }, 33 | 34 | ngx_null_command 35 | }; 36 | 37 | 38 | static ngx_rtmp_module_t ngx_rtmp_limit_module_ctx = { 39 | NULL, /* preconfiguration */ 40 | ngx_rtmp_limit_postconfiguration, /* postconfiguration */ 41 | ngx_rtmp_limit_create_main_conf, /* create main configuration */ 42 | NULL, /* init main configuration */ 43 | NULL, /* create server configuration */ 44 | NULL, /* merge server configuration */ 45 | NULL, /* create app configuration */ 46 | NULL /* merge app configuration */ 47 | }; 48 | 49 | 50 | ngx_module_t ngx_rtmp_limit_module = { 51 | NGX_MODULE_V1, 52 | &ngx_rtmp_limit_module_ctx, /* module context */ 53 | ngx_rtmp_limit_commands, /* module directives */ 54 | NGX_RTMP_MODULE, /* module type */ 55 | NULL, /* init master */ 56 | NULL, /* init module */ 57 | NULL, /* init process */ 58 | NULL, /* init thread */ 59 | NULL, /* exit thread */ 60 | NULL, /* exit process */ 61 | NULL, /* exit master */ 62 | NGX_MODULE_V1_PADDING 63 | }; 64 | 65 | 66 | static void * 67 | ngx_rtmp_limit_create_main_conf(ngx_conf_t *cf) 68 | { 69 | ngx_rtmp_limit_main_conf_t *lmcf; 70 | 71 | lmcf = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_limit_main_conf_t)); 72 | if (lmcf == NULL) { 73 | return NULL; 74 | } 75 | 76 | lmcf->max_conn = NGX_CONF_UNSET; 77 | 78 | return lmcf; 79 | } 80 | 81 | 82 | static ngx_int_t 83 | ngx_rtmp_limit_connect(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, 84 | ngx_chain_t *in) 85 | { 86 | ngx_rtmp_limit_main_conf_t *lmcf; 87 | ngx_slab_pool_t *shpool; 88 | ngx_shm_zone_t *shm_zone; 89 | uint32_t *nconn, n; 90 | ngx_int_t rc; 91 | 92 | lmcf = ngx_rtmp_get_module_main_conf(s, ngx_rtmp_limit_module); 93 | if (lmcf->max_conn == NGX_CONF_UNSET) { 94 | return NGX_OK; 95 | } 96 | 97 | shm_zone = lmcf->shm_zone; 98 | shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; 99 | nconn = shm_zone->data; 100 | 101 | ngx_shmtx_lock(&shpool->mutex); 102 | n = ++*nconn; 103 | ngx_shmtx_unlock(&shpool->mutex); 104 | 105 | rc = n > (ngx_uint_t) lmcf->max_conn ? NGX_ERROR : NGX_OK; 106 | 107 | ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, 108 | "limit: inc conection counter: %uD", n); 109 | 110 | if (rc != NGX_OK) { 111 | ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, 112 | "limit: too many connections: %uD > %i", 113 | n, lmcf->max_conn); 114 | } 115 | 116 | return rc; 117 | } 118 | 119 | 120 | static ngx_int_t 121 | ngx_rtmp_limit_disconnect(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, 122 | ngx_chain_t *in) 123 | { 124 | ngx_rtmp_limit_main_conf_t *lmcf; 125 | ngx_slab_pool_t *shpool; 126 | ngx_shm_zone_t *shm_zone; 127 | uint32_t *nconn, n; 128 | 129 | lmcf = ngx_rtmp_get_module_main_conf(s, ngx_rtmp_limit_module); 130 | if (lmcf->max_conn == NGX_CONF_UNSET) { 131 | return NGX_OK; 132 | } 133 | 134 | shm_zone = lmcf->shm_zone; 135 | shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; 136 | nconn = shm_zone->data; 137 | 138 | ngx_shmtx_lock(&shpool->mutex); 139 | n = --*nconn; 140 | ngx_shmtx_unlock(&shpool->mutex); 141 | 142 | (void) n; 143 | ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, 144 | "limit: dec conection counter: %uD", n); 145 | 146 | return NGX_OK; 147 | } 148 | 149 | 150 | static ngx_int_t 151 | ngx_rtmp_limit_shm_init(ngx_shm_zone_t *shm_zone, void *data) 152 | { 153 | ngx_slab_pool_t *shpool; 154 | uint32_t *nconn; 155 | 156 | if (data) { 157 | shm_zone->data = data; 158 | return NGX_OK; 159 | } 160 | 161 | shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; 162 | 163 | nconn = ngx_slab_alloc(shpool, 4); 164 | if (nconn == NULL) { 165 | return NGX_ERROR; 166 | } 167 | 168 | *nconn = 0; 169 | 170 | shm_zone->data = nconn; 171 | 172 | return NGX_OK; 173 | } 174 | 175 | 176 | static ngx_int_t 177 | ngx_rtmp_limit_postconfiguration(ngx_conf_t *cf) 178 | { 179 | ngx_rtmp_core_main_conf_t *cmcf; 180 | ngx_rtmp_limit_main_conf_t *lmcf; 181 | ngx_rtmp_handler_pt *h; 182 | 183 | cmcf = ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_core_module); 184 | 185 | h = ngx_array_push(&cmcf->events[NGX_RTMP_CONNECT]); 186 | *h = ngx_rtmp_limit_connect; 187 | 188 | h = ngx_array_push(&cmcf->events[NGX_RTMP_DISCONNECT]); 189 | *h = ngx_rtmp_limit_disconnect; 190 | 191 | lmcf = ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_limit_module); 192 | if (lmcf->max_conn == NGX_CONF_UNSET) { 193 | return NGX_OK; 194 | } 195 | 196 | lmcf->shm_zone = ngx_shared_memory_add(cf, &shm_name, ngx_pagesize * 2, 197 | &ngx_rtmp_limit_module); 198 | if (lmcf->shm_zone == NULL) { 199 | return NGX_ERROR; 200 | } 201 | 202 | lmcf->shm_zone->init = ngx_rtmp_limit_shm_init; 203 | 204 | return NGX_OK; 205 | } 206 | -------------------------------------------------------------------------------- /ngx_rtmp_live_module.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #ifndef _NGX_RTMP_LIVE_H_INCLUDED_ 8 | #define _NGX_RTMP_LIVE_H_INCLUDED_ 9 | 10 | 11 | #include 12 | #include 13 | #include "ngx_rtmp.h" 14 | #include "ngx_rtmp_cmd_module.h" 15 | #include "ngx_rtmp_bandwidth.h" 16 | #include "ngx_rtmp_streams.h" 17 | 18 | 19 | typedef struct ngx_rtmp_live_ctx_s ngx_rtmp_live_ctx_t; 20 | typedef struct ngx_rtmp_live_stream_s ngx_rtmp_live_stream_t; 21 | 22 | 23 | typedef struct { 24 | unsigned active:1; 25 | uint32_t timestamp; 26 | uint32_t csid; 27 | uint32_t dropped; 28 | } ngx_rtmp_live_chunk_stream_t; 29 | 30 | 31 | struct ngx_rtmp_live_ctx_s { 32 | ngx_rtmp_session_t *session; 33 | ngx_rtmp_live_stream_t *stream; 34 | ngx_rtmp_live_ctx_t *next; 35 | ngx_uint_t ndropped; 36 | ngx_rtmp_live_chunk_stream_t cs[3]; 37 | ngx_uint_t meta_version; 38 | ngx_event_t idle_evt; 39 | unsigned active:1; 40 | unsigned publishing:1; 41 | unsigned silent:1; 42 | unsigned paused:1; 43 | }; 44 | 45 | 46 | struct ngx_rtmp_live_stream_s { 47 | u_char name[NGX_RTMP_MAX_NAME]; 48 | ngx_rtmp_live_stream_t *next; 49 | ngx_rtmp_live_ctx_t *ctx; 50 | ngx_rtmp_bandwidth_t bw_in; 51 | ngx_rtmp_bandwidth_t bw_in_audio; 52 | ngx_rtmp_bandwidth_t bw_in_video; 53 | ngx_rtmp_bandwidth_t bw_in_data; 54 | ngx_rtmp_bandwidth_t bw_out; 55 | ngx_msec_t epoch; 56 | unsigned active:1; 57 | unsigned publishing:1; 58 | }; 59 | 60 | 61 | typedef struct { 62 | ngx_int_t nbuckets; 63 | ngx_rtmp_live_stream_t **streams; 64 | ngx_flag_t live; 65 | ngx_flag_t meta; 66 | ngx_msec_t sync; 67 | ngx_msec_t idle_timeout; 68 | ngx_flag_t atc; 69 | ngx_flag_t interleave; 70 | ngx_flag_t wait_key; 71 | ngx_flag_t wait_video; 72 | ngx_flag_t publish_notify; 73 | ngx_flag_t play_restart; 74 | ngx_flag_t idle_streams; 75 | ngx_flag_t buffer; 76 | ngx_pool_t *pool; 77 | ngx_rtmp_live_stream_t *free_streams; 78 | } ngx_rtmp_live_app_conf_t; 79 | 80 | 81 | extern ngx_module_t ngx_rtmp_live_module; 82 | 83 | 84 | #endif /* _NGX_RTMP_LIVE_H_INCLUDED_ */ 85 | -------------------------------------------------------------------------------- /ngx_rtmp_netcall_module.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #ifndef _NGX_RTMP_NETCALL_H_INCLUDED_ 8 | #define _NGX_RTMP_NETCALL_H_INCLUDED_ 9 | 10 | 11 | #include 12 | #include 13 | #include "ngx_rtmp.h" 14 | 15 | 16 | typedef ngx_chain_t * (*ngx_rtmp_netcall_create_pt)(ngx_rtmp_session_t *s, 17 | void *arg, ngx_pool_t *pool); 18 | typedef ngx_int_t (*ngx_rtmp_netcall_filter_pt)(ngx_chain_t *in); 19 | typedef ngx_int_t (*ngx_rtmp_netcall_sink_pt)(ngx_rtmp_session_t *s, 20 | ngx_chain_t *in); 21 | typedef ngx_int_t (*ngx_rtmp_netcall_handle_pt)(ngx_rtmp_session_t *s, 22 | void *arg, ngx_chain_t *in); 23 | 24 | #define NGX_RTMP_NETCALL_HTTP_GET 0 25 | #define NGX_RTMP_NETCALL_HTTP_POST 1 26 | 27 | 28 | /* If handle is NULL then netcall is created detached 29 | * which means it's completely independent of RTMP 30 | * session and its result is never visible to anyone. 31 | * 32 | * WARNING: It's not recommended to create non-detached 33 | * netcalls from disconect handlers. Netcall disconnect 34 | * handler which detaches active netcalls is executed 35 | * BEFORE your handler. It leads to a crash 36 | * after netcall connection is closed */ 37 | typedef struct { 38 | ngx_url_t *url; 39 | ngx_rtmp_netcall_create_pt create; 40 | ngx_rtmp_netcall_filter_pt filter; 41 | ngx_rtmp_netcall_sink_pt sink; 42 | ngx_rtmp_netcall_handle_pt handle; 43 | void *arg; 44 | size_t argsize; 45 | } ngx_rtmp_netcall_init_t; 46 | 47 | 48 | ngx_int_t ngx_rtmp_netcall_create(ngx_rtmp_session_t *s, 49 | ngx_rtmp_netcall_init_t *ci); 50 | 51 | 52 | /* HTTP handling */ 53 | ngx_chain_t * ngx_rtmp_netcall_http_format_session(ngx_rtmp_session_t *s, 54 | ngx_pool_t *pool); 55 | ngx_chain_t * ngx_rtmp_netcall_http_format_request(ngx_int_t method, 56 | ngx_str_t *host, ngx_str_t *uri, ngx_chain_t *args, ngx_chain_t *body, 57 | ngx_pool_t *pool, ngx_str_t *content_type); 58 | ngx_chain_t * ngx_rtmp_netcall_http_skip_header(ngx_chain_t *in); 59 | 60 | 61 | /* Memcache handling */ 62 | ngx_chain_t * ngx_rtmp_netcall_memcache_set(ngx_rtmp_session_t *s, 63 | ngx_pool_t *pool, ngx_str_t *key, ngx_str_t *value, 64 | ngx_uint_t flags, ngx_uint_t sec); 65 | 66 | 67 | #endif /* _NGX_RTMP_NETCALL_H_INCLUDED_ */ 68 | -------------------------------------------------------------------------------- /ngx_rtmp_play_module.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #ifndef _NGX_RTMP_PLAY_H_INCLUDED_ 8 | #define _NGX_RTMP_PLAY_H_INCLUDED_ 9 | 10 | 11 | #include 12 | #include 13 | #include "ngx_rtmp.h" 14 | #include "ngx_rtmp_cmd_module.h" 15 | 16 | 17 | typedef ngx_int_t (*ngx_rtmp_play_init_pt) (ngx_rtmp_session_t *s, 18 | ngx_file_t *f, ngx_int_t aindex, ngx_int_t vindex); 19 | typedef ngx_int_t (*ngx_rtmp_play_done_pt) (ngx_rtmp_session_t *s, 20 | ngx_file_t *f); 21 | typedef ngx_int_t (*ngx_rtmp_play_start_pt) (ngx_rtmp_session_t *s, 22 | ngx_file_t *f); 23 | typedef ngx_int_t (*ngx_rtmp_play_seek_pt) (ngx_rtmp_session_t *s, 24 | ngx_file_t *f, ngx_uint_t offs); 25 | typedef ngx_int_t (*ngx_rtmp_play_stop_pt) (ngx_rtmp_session_t *s, 26 | ngx_file_t *f); 27 | typedef ngx_int_t (*ngx_rtmp_play_send_pt) (ngx_rtmp_session_t *s, 28 | ngx_file_t *f, ngx_uint_t *ts); 29 | 30 | 31 | typedef struct { 32 | ngx_str_t name; 33 | ngx_str_t pfx; 34 | ngx_str_t sfx; 35 | 36 | ngx_rtmp_play_init_pt init; 37 | ngx_rtmp_play_done_pt done; 38 | ngx_rtmp_play_start_pt start; 39 | ngx_rtmp_play_seek_pt seek; 40 | ngx_rtmp_play_stop_pt stop; 41 | ngx_rtmp_play_send_pt send; 42 | } ngx_rtmp_play_fmt_t; 43 | 44 | 45 | typedef struct ngx_rtmp_play_ctx_s ngx_rtmp_play_ctx_t; 46 | 47 | 48 | struct ngx_rtmp_play_ctx_s { 49 | ngx_rtmp_session_t *session; 50 | ngx_file_t file; 51 | ngx_rtmp_play_fmt_t *fmt; 52 | ngx_event_t send_evt; 53 | unsigned playing:1; 54 | unsigned opened:1; 55 | unsigned joined:1; 56 | ngx_uint_t ncrs; 57 | ngx_uint_t nheader; 58 | ngx_uint_t nbody; 59 | size_t pfx_size; 60 | ngx_str_t sfx; 61 | ngx_uint_t file_id; 62 | ngx_int_t aindex, vindex; 63 | ngx_uint_t nentry; 64 | ngx_uint_t post_seek; 65 | u_char name[NGX_RTMP_MAX_NAME]; 66 | ngx_rtmp_play_ctx_t *next; 67 | }; 68 | 69 | 70 | typedef struct { 71 | ngx_str_t *root; 72 | ngx_url_t *url; 73 | } ngx_rtmp_play_entry_t; 74 | 75 | 76 | typedef struct { 77 | ngx_str_t temp_path; 78 | ngx_str_t local_path; 79 | ngx_array_t entries; /* ngx_rtmp_play_entry_t * */ 80 | ngx_uint_t nbuckets; 81 | ngx_rtmp_play_ctx_t **ctx; 82 | } ngx_rtmp_play_app_conf_t; 83 | 84 | 85 | typedef struct { 86 | ngx_array_t fmts; /* ngx_rtmp_play_fmt_t * */ 87 | } ngx_rtmp_play_main_conf_t; 88 | 89 | 90 | extern ngx_module_t ngx_rtmp_play_module; 91 | 92 | 93 | #endif /* _NGX_RTMP_PLAY_H_INCLUDED_ */ 94 | -------------------------------------------------------------------------------- /ngx_rtmp_proxy_protocol.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include 10 | #include "ngx_rtmp_proxy_protocol.h" 11 | 12 | 13 | static void ngx_rtmp_proxy_protocol_recv(ngx_event_t *rev); 14 | 15 | 16 | void 17 | ngx_rtmp_proxy_protocol(ngx_rtmp_session_t *s) 18 | { 19 | ngx_event_t *rev; 20 | ngx_connection_t *c; 21 | 22 | c = s->connection; 23 | rev = c->read; 24 | rev->handler = ngx_rtmp_proxy_protocol_recv; 25 | 26 | ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, 27 | "proxy_protocol: start"); 28 | 29 | if (rev->ready) { 30 | /* the deferred accept(), rtsig, aio, iocp */ 31 | 32 | if (ngx_use_accept_mutex) { 33 | ngx_post_event(rev, &ngx_posted_events); 34 | return; 35 | } 36 | 37 | rev->handler(rev); 38 | return; 39 | } 40 | 41 | ngx_add_timer(rev, s->timeout); 42 | 43 | if (ngx_handle_read_event(rev, 0) != NGX_OK) { 44 | ngx_rtmp_finalize_session(s); 45 | return; 46 | } 47 | } 48 | 49 | 50 | static void 51 | ngx_rtmp_proxy_protocol_recv(ngx_event_t *rev) 52 | { 53 | u_char buf[107], *p, *pp, *text; 54 | size_t len; 55 | ssize_t n; 56 | ngx_err_t err; 57 | ngx_int_t i; 58 | ngx_addr_t addr; 59 | ngx_connection_t *c; 60 | ngx_rtmp_session_t *s; 61 | 62 | c = rev->data; 63 | s = c->data; 64 | 65 | if (c->destroyed) { 66 | return; 67 | } 68 | 69 | if (rev->timedout) { 70 | ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, 71 | "proxy_protocol: recv: client timed out"); 72 | c->timedout = 1; 73 | ngx_rtmp_finalize_session(s); 74 | return; 75 | } 76 | 77 | if (rev->timer_set) { 78 | ngx_del_timer(rev); 79 | } 80 | 81 | n = recv(c->fd, (char *) buf, sizeof(buf), MSG_PEEK); 82 | 83 | err = ngx_socket_errno; 84 | 85 | ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "recv(): %d", n); 86 | 87 | if (n == -1) { 88 | 89 | if (err == NGX_EAGAIN) { 90 | ngx_add_timer(rev, s->timeout); 91 | 92 | if (ngx_handle_read_event(c->read, 0) != NGX_OK) { 93 | ngx_rtmp_finalize_session(s); 94 | } 95 | 96 | return; 97 | } 98 | 99 | ngx_rtmp_finalize_session(s); 100 | 101 | return; 102 | } 103 | 104 | p = buf; 105 | 106 | if (n <= 8 && ngx_strncmp(p, "PROXY ", 6) != 0) { 107 | goto bad_header; 108 | } 109 | 110 | n -= 6; 111 | p += 6; 112 | 113 | ngx_memzero(&addr, sizeof(ngx_addr_t)); 114 | 115 | if (n >= 7 && ngx_strncmp(p, "UNKNOWN", 7) == 0) { 116 | n -= 7; 117 | p += 7; 118 | goto skip; 119 | } 120 | 121 | if (n < 5 || ngx_strncmp(p, "TCP", 3) != 0 122 | || (p[3] != '4' && p[3] != '6') || p[4] != ' ') 123 | { 124 | goto bad_header; 125 | } 126 | 127 | n -= 5; 128 | p += 5; 129 | 130 | pp = ngx_strlchr(p, p + n, ' '); 131 | 132 | if (pp == NULL) { 133 | goto bad_header; 134 | } 135 | 136 | if (ngx_parse_addr(s->connection->pool, &addr, p, pp - p) != NGX_OK) { 137 | goto bad_header; 138 | } 139 | 140 | n -= pp - p; 141 | p = pp; 142 | 143 | skip: 144 | 145 | for (i = 0; i + 1 < n; i++) { 146 | if (p[i] == CR && p[i + 1] == LF) { 147 | break; 148 | } 149 | } 150 | 151 | if (i + 1 >= n) { 152 | goto bad_header; 153 | } 154 | 155 | n = p - buf + i + 2; 156 | 157 | if (c->recv(c, buf, n) != n) { 158 | goto failed; 159 | } 160 | 161 | if (addr.socklen) { 162 | text = ngx_palloc(s->connection->pool, NGX_SOCKADDR_STRLEN); 163 | 164 | if (text == NULL) { 165 | goto failed; 166 | } 167 | 168 | len = ngx_sock_ntop(addr.sockaddr, 169 | #if (nginx_version >= 1005003) 170 | addr.socklen, 171 | #endif 172 | text, NGX_SOCKADDR_STRLEN, 0); 173 | if (len == 0) { 174 | goto failed; 175 | } 176 | 177 | c->sockaddr = addr.sockaddr; 178 | c->socklen = addr.socklen; 179 | c->addr_text.data = text; 180 | c->addr_text.len = len; 181 | 182 | ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, 183 | "proxy_protocol: remote_addr:'%V'", &c->addr_text); 184 | } 185 | 186 | ngx_rtmp_handshake(s); 187 | 188 | return; 189 | 190 | bad_header: 191 | 192 | ngx_log_error(NGX_LOG_INFO, c->log, 0, "proxy_protocol: bad header"); 193 | 194 | failed: 195 | 196 | ngx_rtmp_finalize_session(s); 197 | } 198 | -------------------------------------------------------------------------------- /ngx_rtmp_proxy_protocol.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #ifndef _NGX_RTMP_PROXY_PROTOCOL_H_INCLUDED_ 8 | #define _NGX_RTMP_PROXY_PROTOCOL_H_INCLUDED_ 9 | 10 | 11 | #include 12 | #include 13 | #include "ngx_rtmp.h" 14 | 15 | 16 | void ngx_rtmp_proxy_protocol(ngx_rtmp_session_t *c); 17 | 18 | 19 | #endif /* _NGX_RTMP_PROXY_PROTOCOL_H_INCLUDED_ */ 20 | -------------------------------------------------------------------------------- /ngx_rtmp_receive.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include "ngx_rtmp.h" 10 | #include "ngx_rtmp_amf.h" 11 | #include "ngx_rtmp_cmd_module.h" 12 | #include 13 | 14 | 15 | ngx_int_t 16 | ngx_rtmp_protocol_message_handler(ngx_rtmp_session_t *s, 17 | ngx_rtmp_header_t *h, ngx_chain_t *in) 18 | { 19 | ngx_buf_t *b; 20 | u_char *p; 21 | uint32_t val; 22 | uint8_t limit; 23 | 24 | b = in->buf; 25 | 26 | if (b->last - b->pos < 4) { 27 | ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, 28 | "too small buffer for %d message: %d", 29 | (int)h->type, b->last - b->pos); 30 | return NGX_OK; 31 | } 32 | 33 | p = (u_char*)&val; 34 | p[0] = b->pos[3]; 35 | p[1] = b->pos[2]; 36 | p[2] = b->pos[1]; 37 | p[3] = b->pos[0]; 38 | 39 | switch(h->type) { 40 | case NGX_RTMP_MSG_CHUNK_SIZE: 41 | /* set chunk size =val */ 42 | ngx_rtmp_set_chunk_size(s, val); 43 | break; 44 | 45 | case NGX_RTMP_MSG_ABORT: 46 | /* abort chunk stream =val */ 47 | break; 48 | 49 | case NGX_RTMP_MSG_ACK: 50 | /* receive ack with sequence number =val */ 51 | ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, 52 | "receive ack seq=%uD", val); 53 | break; 54 | 55 | case NGX_RTMP_MSG_ACK_SIZE: 56 | /* receive window size =val */ 57 | ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, 58 | "receive ack_size=%uD", val); 59 | s->ack_size = val; 60 | break; 61 | 62 | case NGX_RTMP_MSG_BANDWIDTH: 63 | if (b->last - b->pos >= 5) { 64 | limit = *(uint8_t*)&b->pos[4]; 65 | 66 | (void)val; 67 | (void)limit; 68 | 69 | ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, 70 | "receive bandwidth=%uD limit=%d", 71 | val, (int)limit); 72 | 73 | /* receive window size =val 74 | * && limit */ 75 | } 76 | break; 77 | 78 | default: 79 | return NGX_ERROR; 80 | } 81 | 82 | return NGX_OK; 83 | } 84 | 85 | 86 | ngx_int_t 87 | ngx_rtmp_user_message_handler(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, 88 | ngx_chain_t *in) 89 | { 90 | ngx_buf_t *b; 91 | u_char *p; 92 | uint16_t evt; 93 | uint32_t val; 94 | 95 | b = in->buf; 96 | 97 | if (b->last - b->pos < 6) { 98 | ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, 99 | "too small buffer for user message: %d", 100 | b->last - b->pos); 101 | return NGX_OK; 102 | } 103 | 104 | p = (u_char*)&evt; 105 | 106 | p[0] = b->pos[1]; 107 | p[1] = b->pos[0]; 108 | 109 | ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, 110 | "RTMP recv user evt %s (%i)", 111 | ngx_rtmp_user_message_type(evt), (ngx_int_t) evt); 112 | 113 | p = (u_char *) &val; 114 | 115 | p[0] = b->pos[5]; 116 | p[1] = b->pos[4]; 117 | p[2] = b->pos[3]; 118 | p[3] = b->pos[2]; 119 | 120 | switch(evt) { 121 | case NGX_RTMP_USER_STREAM_BEGIN: 122 | { 123 | ngx_rtmp_stream_begin_t v; 124 | 125 | v.msid = val; 126 | 127 | ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, 128 | "receive: stream_begin msid=%uD", v.msid); 129 | 130 | return ngx_rtmp_stream_begin(s, &v); 131 | } 132 | 133 | case NGX_RTMP_USER_STREAM_EOF: 134 | { 135 | ngx_rtmp_stream_eof_t v; 136 | 137 | v.msid = val; 138 | 139 | ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, 140 | "receive: stream_eof msid=%uD", v.msid); 141 | 142 | return ngx_rtmp_stream_eof(s, &v); 143 | } 144 | 145 | case NGX_RTMP_USER_STREAM_DRY: 146 | { 147 | ngx_rtmp_stream_dry_t v; 148 | 149 | v.msid = val; 150 | 151 | ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, 152 | "receive: stream_dry msid=%uD", v.msid); 153 | 154 | return ngx_rtmp_stream_dry(s, &v); 155 | } 156 | 157 | case NGX_RTMP_USER_SET_BUFLEN: 158 | { 159 | ngx_rtmp_set_buflen_t v; 160 | 161 | v.msid = val; 162 | 163 | if (b->last - b->pos < 10) { 164 | return NGX_OK; 165 | } 166 | 167 | p = (u_char *) &v.buflen; 168 | 169 | p[0] = b->pos[9]; 170 | p[1] = b->pos[8]; 171 | p[2] = b->pos[7]; 172 | p[3] = b->pos[6]; 173 | 174 | ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, 175 | "receive: set_buflen msid=%uD buflen=%uD", 176 | v.msid, v.buflen); 177 | 178 | /*TODO: move this to play module */ 179 | s->buflen = v.buflen; 180 | 181 | return ngx_rtmp_set_buflen(s, &v); 182 | } 183 | 184 | case NGX_RTMP_USER_RECORDED: 185 | { 186 | ngx_rtmp_recorded_t v; 187 | 188 | v.msid = val; 189 | 190 | ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, 191 | "receive: recorded msid=%uD", v.msid); 192 | 193 | return ngx_rtmp_recorded(s, &v); 194 | } 195 | 196 | case NGX_RTMP_USER_PING_REQUEST: 197 | return ngx_rtmp_send_ping_response(s, val); 198 | 199 | case NGX_RTMP_USER_PING_RESPONSE: 200 | 201 | /* val = incoming timestamp */ 202 | 203 | ngx_rtmp_reset_ping(s); 204 | 205 | return NGX_OK; 206 | 207 | default: 208 | ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, 209 | "unexpected user event: %i", (ngx_int_t) evt); 210 | 211 | return NGX_OK; 212 | } 213 | } 214 | 215 | 216 | static ngx_int_t 217 | ngx_rtmp_fetch(ngx_chain_t **in, u_char *ret) 218 | { 219 | while (*in && (*in)->buf->pos >= (*in)->buf->last) { 220 | *in = (*in)->next; 221 | } 222 | 223 | if (*in == NULL) { 224 | return NGX_DONE; 225 | } 226 | 227 | *ret = *(*in)->buf->pos++; 228 | 229 | return NGX_OK; 230 | } 231 | 232 | 233 | static ngx_int_t 234 | ngx_rtmp_fetch_uint8(ngx_chain_t **in, uint8_t *ret) 235 | { 236 | return ngx_rtmp_fetch(in, (u_char *) ret); 237 | } 238 | 239 | 240 | static ngx_int_t 241 | ngx_rtmp_fetch_uint32(ngx_chain_t **in, uint32_t *ret, ngx_int_t n) 242 | { 243 | u_char *r = (u_char *) ret; 244 | ngx_int_t rc; 245 | 246 | *ret = 0; 247 | 248 | while (--n >= 0) { 249 | rc = ngx_rtmp_fetch(in, &r[n]); 250 | if (rc != NGX_OK) { 251 | return rc; 252 | } 253 | } 254 | 255 | return NGX_OK; 256 | } 257 | 258 | 259 | ngx_int_t 260 | ngx_rtmp_aggregate_message_handler(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, 261 | ngx_chain_t *in) 262 | { 263 | uint32_t base_time, timestamp, prev_size; 264 | size_t len; 265 | ngx_int_t first; 266 | u_char *last; 267 | ngx_int_t rc; 268 | ngx_buf_t *b; 269 | ngx_chain_t *cl, *next; 270 | ngx_rtmp_header_t ch; 271 | 272 | ch = *h; 273 | 274 | first = 1; 275 | base_time = 0; 276 | 277 | while (in) { 278 | if (ngx_rtmp_fetch_uint8(&in, &ch.type) != NGX_OK) { 279 | return NGX_OK; 280 | } 281 | 282 | if (ngx_rtmp_fetch_uint32(&in, &ch.mlen, 3) != NGX_OK) { 283 | return NGX_ERROR; 284 | } 285 | 286 | if (ngx_rtmp_fetch_uint32(&in, ×tamp, 3) != NGX_OK) { 287 | return NGX_ERROR; 288 | } 289 | 290 | if (ngx_rtmp_fetch_uint8(&in, (uint8_t *) ×tamp + 3) != NGX_OK) 291 | { 292 | return NGX_ERROR; 293 | } 294 | 295 | if (ngx_rtmp_fetch_uint32(&in, &ch.msid, 3) != NGX_OK) 296 | { 297 | return NGX_ERROR; 298 | } 299 | 300 | if (first) { 301 | base_time = timestamp; 302 | first = 0; 303 | } 304 | 305 | ngx_log_debug6(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, 306 | "RTMP aggregate %s (%d) len=%uD time=%uD (+%D) msid=%uD", 307 | ngx_rtmp_message_type(ch.type), 308 | (ngx_int_t) ch.type, ch.mlen, ch.timestamp, 309 | timestamp - base_time, ch.msid); 310 | 311 | /* limit chain */ 312 | 313 | len = 0; 314 | cl = in; 315 | while (cl) { 316 | b = cl->buf; 317 | len += (b->last - b->pos); 318 | if (len > ch.mlen) { 319 | break; 320 | } 321 | cl = cl->next; 322 | } 323 | 324 | if (cl == NULL) { 325 | ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, 326 | "RTMP error parsing aggregate"); 327 | return NGX_ERROR; 328 | } 329 | 330 | next = cl->next; 331 | cl->next = NULL; 332 | b = cl->buf; 333 | last = b->last; 334 | b->last -= (len - ch.mlen); 335 | 336 | /* handle aggregated message */ 337 | 338 | ch.timestamp = h->timestamp + timestamp - base_time; 339 | 340 | rc = ngx_rtmp_receive_message(s, &ch, in); 341 | 342 | /* restore chain before checking the result */ 343 | 344 | in = cl; 345 | in->next = next; 346 | b->pos = b->last; 347 | b->last = last; 348 | 349 | if (rc != NGX_OK) { 350 | return rc; 351 | } 352 | 353 | /* read 32-bit previous tag size */ 354 | 355 | if (ngx_rtmp_fetch_uint32(&in, &prev_size, 4) != NGX_OK) { 356 | return NGX_OK; 357 | } 358 | 359 | ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, 360 | "RTMP aggregate prev_size=%uD", prev_size); 361 | } 362 | 363 | return NGX_OK; 364 | } 365 | 366 | 367 | ngx_int_t 368 | ngx_rtmp_amf_message_handler(ngx_rtmp_session_t *s, 369 | ngx_rtmp_header_t *h, ngx_chain_t *in) 370 | { 371 | ngx_rtmp_amf_ctx_t act; 372 | ngx_rtmp_core_main_conf_t *cmcf; 373 | ngx_array_t *ch; 374 | ngx_rtmp_handler_pt *ph; 375 | size_t len, n; 376 | 377 | static u_char func[128]; 378 | 379 | static ngx_rtmp_amf_elt_t elts[] = { 380 | 381 | { NGX_RTMP_AMF_STRING, 382 | ngx_null_string, 383 | func, sizeof(func) }, 384 | }; 385 | 386 | /* AMF command names come with string type, but shared object names 387 | * come without type */ 388 | if (h->type == NGX_RTMP_MSG_AMF_SHARED || 389 | h->type == NGX_RTMP_MSG_AMF3_SHARED) 390 | { 391 | elts[0].type |= NGX_RTMP_AMF_TYPELESS; 392 | } else { 393 | elts[0].type &= ~NGX_RTMP_AMF_TYPELESS; 394 | } 395 | 396 | if ((h->type == NGX_RTMP_MSG_AMF3_SHARED || 397 | h->type == NGX_RTMP_MSG_AMF3_META || 398 | h->type == NGX_RTMP_MSG_AMF3_CMD) 399 | && in->buf->last > in->buf->pos) 400 | { 401 | ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, 402 | "AMF3 prefix: %ui", (ngx_int_t)*in->buf->pos); 403 | ++in->buf->pos; 404 | } 405 | 406 | cmcf = ngx_rtmp_get_module_main_conf(s, ngx_rtmp_core_module); 407 | 408 | /* read AMF func name & transaction id */ 409 | ngx_memzero(&act, sizeof(act)); 410 | act.link = in; 411 | act.log = s->connection->log; 412 | memset(func, 0, sizeof(func)); 413 | 414 | if (ngx_rtmp_amf_read(&act, elts, 415 | sizeof(elts) / sizeof(elts[0])) != NGX_OK) 416 | { 417 | ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, 418 | "AMF cmd failed"); 419 | return NGX_ERROR; 420 | } 421 | 422 | /* skip name */ 423 | in = act.link; 424 | in->buf->pos += act.offset; 425 | 426 | len = ngx_strlen(func); 427 | 428 | ch = ngx_hash_find(&cmcf->amf_hash, 429 | ngx_hash_strlow(func, func, len), func, len); 430 | 431 | if (ch && ch->nelts) { 432 | ph = ch->elts; 433 | for (n = 0; n < ch->nelts; ++n, ++ph) { 434 | ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, 435 | "AMF func '%s' passed to handler %d/%d", 436 | func, n, ch->nelts); 437 | switch ((*ph)(s, h, in)) { 438 | case NGX_ERROR: 439 | return NGX_ERROR; 440 | case NGX_DONE: 441 | return NGX_OK; 442 | } 443 | } 444 | } else { 445 | ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, 446 | "AMF cmd '%s' no handler", func); 447 | } 448 | 449 | return NGX_OK; 450 | } 451 | 452 | 453 | ngx_int_t 454 | ngx_rtmp_receive_amf(ngx_rtmp_session_t *s, ngx_chain_t *in, 455 | ngx_rtmp_amf_elt_t *elts, size_t nelts) 456 | { 457 | ngx_rtmp_amf_ctx_t act; 458 | 459 | ngx_memzero(&act, sizeof(act)); 460 | act.link = in; 461 | act.log = s->connection->log; 462 | 463 | return ngx_rtmp_amf_read(&act, elts, nelts); 464 | } 465 | -------------------------------------------------------------------------------- /ngx_rtmp_record_module.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #ifndef _NGX_RTMP_RECORD_H_INCLUDED_ 8 | #define _NGX_RTMP_RECORD_H_INCLUDED_ 9 | 10 | 11 | #include 12 | #include 13 | #include "ngx_rtmp.h" 14 | 15 | 16 | #define NGX_RTMP_RECORD_OFF 0x01 17 | #define NGX_RTMP_RECORD_AUDIO 0x02 18 | #define NGX_RTMP_RECORD_VIDEO 0x04 19 | #define NGX_RTMP_RECORD_DATA 0x08 20 | #define NGX_RTMP_RECORD_KEYFRAMES 0x10 21 | #define NGX_RTMP_RECORD_MANUAL 0x20 22 | 23 | typedef struct { 24 | ngx_str_t id; 25 | ngx_uint_t flags; 26 | ngx_str_t path; 27 | size_t max_size; 28 | size_t interval_size; 29 | size_t max_frames; 30 | ngx_msec_t interval; 31 | ngx_str_t suffix; 32 | ngx_flag_t unique; 33 | ngx_flag_t append; 34 | ngx_flag_t lock_file; 35 | ngx_flag_t notify; 36 | ngx_url_t *url; 37 | 38 | void **rec_conf; 39 | ngx_array_t rec; /* ngx_rtmp_record_app_conf_t * */ 40 | } ngx_rtmp_record_app_conf_t; 41 | 42 | 43 | typedef struct { 44 | ngx_rtmp_record_app_conf_t *conf; 45 | ngx_file_t file; 46 | ngx_uint_t nframes; 47 | uint32_t epoch, time_shift; 48 | ngx_time_t last; 49 | time_t timestamp; 50 | unsigned failed:1; 51 | unsigned initialized:1; 52 | unsigned aac_header_sent:1; 53 | unsigned avc_header_sent:1; 54 | unsigned video_key_sent:1; 55 | unsigned audio:1; 56 | unsigned video:1; 57 | unsigned record_started:1; 58 | } ngx_rtmp_record_rec_ctx_t; 59 | 60 | 61 | typedef struct { 62 | ngx_array_t rec; /* ngx_rtmp_record_rec_ctx_t */ 63 | u_char name[NGX_RTMP_MAX_NAME]; 64 | u_char args[NGX_RTMP_MAX_ARGS]; 65 | } ngx_rtmp_record_ctx_t; 66 | 67 | 68 | ngx_uint_t ngx_rtmp_record_find(ngx_rtmp_record_app_conf_t *racf, 69 | ngx_str_t *id); 70 | 71 | 72 | /* Manual recording control, 73 | * 'n' is record node index in config array. 74 | * Note: these functions allocate path in static buffer */ 75 | 76 | ngx_int_t ngx_rtmp_record_open(ngx_rtmp_session_t *s, ngx_uint_t n, 77 | ngx_str_t *path); 78 | ngx_int_t ngx_rtmp_record_close(ngx_rtmp_session_t *s, ngx_uint_t n, 79 | ngx_str_t *path); 80 | 81 | 82 | typedef struct { 83 | ngx_str_t recorder; 84 | ngx_str_t path; 85 | } ngx_rtmp_record_done_t; 86 | 87 | 88 | typedef struct { 89 | ngx_str_t recorder; 90 | ngx_str_t path; 91 | } ngx_rtmp_record_started_t; 92 | 93 | 94 | typedef ngx_int_t (*ngx_rtmp_record_started_pt)(ngx_rtmp_session_t *s, 95 | ngx_rtmp_record_started_t *v); 96 | 97 | 98 | typedef ngx_int_t (*ngx_rtmp_record_done_pt)(ngx_rtmp_session_t *s, 99 | ngx_rtmp_record_done_t *v); 100 | 101 | 102 | extern ngx_rtmp_record_started_pt ngx_rtmp_record_started; 103 | 104 | 105 | extern ngx_rtmp_record_done_pt ngx_rtmp_record_done; 106 | 107 | 108 | extern ngx_module_t ngx_rtmp_record_module; 109 | 110 | 111 | #endif /* _NGX_RTMP_RECORD_H_INCLUDED_ */ 112 | -------------------------------------------------------------------------------- /ngx_rtmp_relay_module.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #ifndef _NGX_RTMP_RELAY_H_INCLUDED_ 8 | #define _NGX_RTMP_RELAY_H_INCLUDED_ 9 | 10 | 11 | #include 12 | #include 13 | #include "ngx_rtmp.h" 14 | 15 | 16 | typedef struct { 17 | ngx_url_t url; 18 | ngx_str_t app; 19 | ngx_str_t name; 20 | ngx_str_t tc_url; 21 | ngx_str_t page_url; 22 | ngx_str_t swf_url; 23 | ngx_str_t flash_ver; 24 | ngx_str_t play_path; 25 | ngx_int_t live; 26 | ngx_int_t start; 27 | ngx_int_t stop; 28 | 29 | void *tag; /* usually module reference */ 30 | void *data; /* module-specific data */ 31 | ngx_uint_t counter; /* mutable connection counter */ 32 | } ngx_rtmp_relay_target_t; 33 | 34 | 35 | typedef struct ngx_rtmp_relay_ctx_s ngx_rtmp_relay_ctx_t; 36 | 37 | struct ngx_rtmp_relay_ctx_s { 38 | ngx_str_t name; 39 | ngx_str_t url; 40 | ngx_log_t log; 41 | ngx_rtmp_session_t *session; 42 | ngx_rtmp_relay_ctx_t *publish; 43 | ngx_rtmp_relay_ctx_t *play; 44 | ngx_rtmp_relay_ctx_t *next; 45 | 46 | ngx_str_t app; 47 | ngx_str_t tc_url; 48 | ngx_str_t page_url; 49 | ngx_str_t swf_url; 50 | ngx_str_t flash_ver; 51 | ngx_str_t play_path; 52 | ngx_int_t live; 53 | ngx_int_t start; 54 | ngx_int_t stop; 55 | 56 | ngx_event_t push_evt; 57 | ngx_event_t *static_evt; 58 | void *tag; 59 | void *data; 60 | }; 61 | 62 | 63 | extern ngx_module_t ngx_rtmp_relay_module; 64 | 65 | 66 | ngx_int_t ngx_rtmp_relay_pull(ngx_rtmp_session_t *s, ngx_str_t *name, 67 | ngx_rtmp_relay_target_t *target); 68 | ngx_int_t ngx_rtmp_relay_push(ngx_rtmp_session_t *s, ngx_str_t *name, 69 | ngx_rtmp_relay_target_t *target); 70 | 71 | 72 | #endif /* _NGX_RTMP_RELAY_H_INCLUDED_ */ 73 | -------------------------------------------------------------------------------- /ngx_rtmp_shared.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include "ngx_rtmp.h" 10 | 11 | 12 | ngx_chain_t * 13 | ngx_rtmp_alloc_shared_buf(ngx_rtmp_core_srv_conf_t *cscf) 14 | { 15 | u_char *p; 16 | ngx_chain_t *out; 17 | ngx_buf_t *b; 18 | size_t size; 19 | 20 | if (cscf->free) { 21 | out = cscf->free; 22 | cscf->free = out->next; 23 | 24 | } else { 25 | 26 | size = cscf->chunk_size + NGX_RTMP_MAX_CHUNK_HEADER; 27 | 28 | p = ngx_pcalloc(cscf->pool, NGX_RTMP_REFCOUNT_BYTES 29 | + sizeof(ngx_chain_t) 30 | + sizeof(ngx_buf_t) 31 | + size); 32 | if (p == NULL) { 33 | return NULL; 34 | } 35 | 36 | p += NGX_RTMP_REFCOUNT_BYTES; 37 | out = (ngx_chain_t *)p; 38 | 39 | p += sizeof(ngx_chain_t); 40 | out->buf = (ngx_buf_t *)p; 41 | 42 | p += sizeof(ngx_buf_t); 43 | out->buf->start = p; 44 | out->buf->end = p + size; 45 | } 46 | 47 | out->next = NULL; 48 | b = out->buf; 49 | b->pos = b->last = b->start + NGX_RTMP_MAX_CHUNK_HEADER; 50 | b->memory = 1; 51 | 52 | /* buffer has refcount =1 when created! */ 53 | ngx_rtmp_ref_set(out, 1); 54 | 55 | return out; 56 | } 57 | 58 | 59 | void 60 | ngx_rtmp_free_shared_chain(ngx_rtmp_core_srv_conf_t *cscf, ngx_chain_t *in) 61 | { 62 | ngx_chain_t *cl; 63 | 64 | if (ngx_rtmp_ref_put(in)) { 65 | return; 66 | } 67 | 68 | for (cl = in; ; cl = cl->next) { 69 | /* FIXME: Don't create circular chains in the first place */ 70 | if (cl->next == NULL || cl->next == in) { 71 | cl->next = cscf->free; 72 | cscf->free = in; 73 | return; 74 | } 75 | } 76 | } 77 | 78 | 79 | ngx_chain_t * 80 | ngx_rtmp_append_shared_bufs(ngx_rtmp_core_srv_conf_t *cscf, 81 | ngx_chain_t *head, ngx_chain_t *in) 82 | { 83 | ngx_chain_t *l, **ll; 84 | u_char *p; 85 | size_t size; 86 | 87 | ll = &head; 88 | p = in->buf->pos; 89 | l = head; 90 | 91 | if (l) { 92 | for(; l->next; l = l->next); 93 | ll = &l->next; 94 | } 95 | 96 | for ( ;; ) { 97 | 98 | if (l == NULL || l->buf->last == l->buf->end) { 99 | l = ngx_rtmp_alloc_shared_buf(cscf); 100 | if (l == NULL || l->buf == NULL) { 101 | break; 102 | } 103 | 104 | *ll = l; 105 | ll = &l->next; 106 | } 107 | 108 | while (l->buf->end - l->buf->last >= in->buf->last - p) { 109 | l->buf->last = ngx_cpymem(l->buf->last, p, 110 | in->buf->last - p); 111 | in = in->next; 112 | if (in == NULL) { 113 | goto done; 114 | } 115 | p = in->buf->pos; 116 | } 117 | 118 | size = l->buf->end - l->buf->last; 119 | l->buf->last = ngx_cpymem(l->buf->last, p, size); 120 | p += size; 121 | } 122 | 123 | done: 124 | *ll = NULL; 125 | 126 | return head; 127 | } 128 | -------------------------------------------------------------------------------- /ngx_rtmp_streams.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #ifndef _NGX_RTMP_STREAMS_H_INCLUDED_ 8 | #define _NGX_RTMP_STREAMS_H_INCLUDED_ 9 | 10 | 11 | #define NGX_RTMP_MSID 1 12 | 13 | #define NGX_RTMP_CSID_AMF_INI 3 14 | #define NGX_RTMP_CSID_AMF 5 15 | #define NGX_RTMP_CSID_AUDIO 6 16 | #define NGX_RTMP_CSID_VIDEO 7 17 | 18 | 19 | #endif /* _NGX_RTMP_STREAMS_H_INCLUDED_ */ 20 | -------------------------------------------------------------------------------- /ngx_rtmp_version.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | */ 5 | 6 | 7 | #ifndef _NGX_RTMP_VERSION_H_INCLUDED_ 8 | #define _NGX_RTMP_VERSION_H_INCLUDED_ 9 | 10 | 11 | #define nginx_rtmp_version 1001007 12 | #define NGINX_RTMP_VERSION "1.1.7.11-dev" 13 | 14 | 15 | #endif /* _NGX_RTMP_VERSION_H_INCLUDED_ */ 16 | -------------------------------------------------------------------------------- /stat.xsl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | RTMP statistics 16 | 17 | 18 | 19 |
20 | Generated by 21 | nginx-rtmp-module , 22 | nginx , 23 | pid , 24 | built   25 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 59 | 64 | 71 | 78 | 85 | 86 | 87 |
RTMP#clientsVideoAudioIn bytesOut bytesIn bits/sOut bits/sStateRecordTime
Accepted: codecbits/ssizefpscodecbits/sfreqchan 55 | 56 | 57 | 58 | 60 | 61 | 62 | 63 | 65 | 66 | 67 | 68 | 69 | 70 | 72 | 73 | 74 | 75 | 76 | 77 | 79 | 80 | 81 | 82 | 83 | 84 |
88 |
89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | live streams 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | vod streams 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | #cccccc 133 | #dddddd 134 | 135 | 136 | 137 | 138 | 139 | var d=document.getElementById('-'); 140 | d.style.display=d.style.display=='none'?'':'none'; 141 | return false 142 | 143 | 144 | 145 | [EMPTY] 146 | 147 | 148 | 149 | 150 | 151 |    152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 |   168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | - 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 |
IdStateRecordingAddressFlash versionPage URLSWF URLDroppedTimestampA-VTime
235 | 236 | 237 |
238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | d 249 | 250 | 251 | 252 | h 253 | 254 | 255 | 256 | m 257 | 258 | 259 | s 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | T 273 | 274 | 275 | G 276 | 277 | 278 | M 279 | 280 | K 281 | 282 | 283 | 284 | b 285 | B 286 | 287 | /s 288 | 289 | 290 | 291 | 292 | 293 | active 294 | idle 295 | 296 | 297 | 298 | 299 | 300 | 301 | publishing 302 | playing 303 | 304 | 305 | 306 | 307 | 308 | yes 309 | no 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | #cccccc 319 | #eeeeee 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | http://apps.db.ripe.net/search/query.html?searchtext= 329 | 330 | whois 331 | : 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | publishing 357 | 358 | 359 | 360 | active 361 | 362 | 363 | 364 | recording 365 | 366 | 367 | 368 | x 369 | 370 | 371 |
372 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # RTMP tests 2 | 3 | nginx.conf is sample config for testing nginx-rtmp. 4 | Please update paths in it before using. 5 | 6 | RTMP port: 1935, HTTP port: 8080 7 | 8 | * http://localhost:8080/ - play myapp/mystream with JWPlayer 9 | * http://localhost:8080/record.html - capture myapp/mystream from webcam with old JWPlayer 10 | * http://localhost:8080/rtmp-publisher/player.html - play myapp/mystream with the test flash applet 11 | * http://localhost:8080/rtmp-publisher/publisher.html - capture myapp/mystream with the test flash applet 12 | -------------------------------------------------------------------------------- /test/dump.sh: -------------------------------------------------------------------------------- 1 | rtmpdump -v -r "rtmp://localhost/myapp/mystream" 2 | -------------------------------------------------------------------------------- /test/ffstream.sh: -------------------------------------------------------------------------------- 1 | ffmpeg -loglevel verbose -re -i ~/movie.avi -f flv rtmp://localhost/myapp/mystream 2 | -------------------------------------------------------------------------------- /test/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 1; 2 | 3 | error_log logs/error.log debug; 4 | 5 | events { 6 | worker_connections 1024; 7 | } 8 | 9 | rtmp { 10 | server { 11 | listen 1935; 12 | 13 | application myapp { 14 | live on; 15 | 16 | #record keyframes; 17 | #record_path /tmp; 18 | #record_max_size 128K; 19 | #record_interval 30s; 20 | #record_suffix .this.is.flv; 21 | 22 | #on_publish http://localhost:8080/publish; 23 | #on_play http://localhost:8080/play; 24 | #on_record_done http://localhost:8080/record_done; 25 | } 26 | } 27 | } 28 | 29 | http { 30 | server { 31 | listen 8080; 32 | 33 | location /stat { 34 | rtmp_stat all; 35 | rtmp_stat_stylesheet stat.xsl; 36 | } 37 | 38 | location /stat.xsl { 39 | root /path/to/nginx-rtmp-module/; 40 | } 41 | 42 | location /control { 43 | rtmp_control all; 44 | } 45 | 46 | #location /publish { 47 | # return 201; 48 | #} 49 | 50 | #location /play { 51 | # return 202; 52 | #} 53 | 54 | #location /record_done { 55 | # return 203; 56 | #} 57 | 58 | location /rtmp-publisher { 59 | root /path/to/nginx-rtmp-module/test; 60 | } 61 | 62 | location / { 63 | root /path/to/nginx-rtmp-module/test/www; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /test/play.sh: -------------------------------------------------------------------------------- 1 | ffplay -loglevel verbose "rtmp://localhost/myapp/mystream" 2 | -------------------------------------------------------------------------------- /test/rtmp-publisher/README.md: -------------------------------------------------------------------------------- 1 | # RTMP Publisher 2 | 3 | Simple RTMP publisher. 4 | 5 | Edit the following flashvars in publisher.html & player.html to suite your needs. 6 | 7 | streamer: RTMP endpoint 8 | file: live stream name 9 | 10 | ## Compile 11 | 12 | Install flex sdk http://www.adobe.com/devnet/flex/flex-sdk-download.html 13 | 14 | mxmlc RtmpPublisher.mxml 15 | mxmlc RtmpPlayer.mxml 16 | -------------------------------------------------------------------------------- /test/rtmp-publisher/RtmpPlayer.mxml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /test/rtmp-publisher/RtmpPlayer.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ut0mt8/nginx-rtmp-module/f0ea62342a4eca504b311cd5df910d026c3ea4cf/test/rtmp-publisher/RtmpPlayer.swf -------------------------------------------------------------------------------- /test/rtmp-publisher/RtmpPlayerLight.mxml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /test/rtmp-publisher/RtmpPlayerLight.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ut0mt8/nginx-rtmp-module/f0ea62342a4eca504b311cd5df910d026c3ea4cf/test/rtmp-publisher/RtmpPlayerLight.swf -------------------------------------------------------------------------------- /test/rtmp-publisher/RtmpPublisher.mxml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /test/rtmp-publisher/RtmpPublisher.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ut0mt8/nginx-rtmp-module/f0ea62342a4eca504b311cd5df910d026c3ea4cf/test/rtmp-publisher/RtmpPublisher.swf -------------------------------------------------------------------------------- /test/rtmp-publisher/player.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RTMP Player 5 | 6 | 16 | 17 | 18 |
19 |

Flash not installed

20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /test/rtmp-publisher/publisher.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RTMP Publisher 5 | 6 | 13 | 14 | 15 |
16 |

Flash not installed

17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /test/rtmp-publisher/swfobject.js: -------------------------------------------------------------------------------- 1 | /* SWFObject v2.2 2 | is released under the MIT License 3 | */ 4 | var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='"+af+"";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;abPlay | Record 2 |
3 | 4 | 5 |
Loading the player ...
6 | 20 | -------------------------------------------------------------------------------- /test/www/jwplayer/jwplayer.flash.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ut0mt8/nginx-rtmp-module/f0ea62342a4eca504b311cd5df910d026c3ea4cf/test/www/jwplayer/jwplayer.flash.swf -------------------------------------------------------------------------------- /test/www/jwplayer_old/player.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ut0mt8/nginx-rtmp-module/f0ea62342a4eca504b311cd5df910d026c3ea4cf/test/www/jwplayer_old/player.swf -------------------------------------------------------------------------------- /test/www/jwplayer_old/swfobject.js: -------------------------------------------------------------------------------- 1 | /* SWFObject v2.1 2 | Copyright (c) 2007-2008 Geoff Stearns, Michael Williams, and Bobby van der Sluis 3 | This software is released under the MIT License 4 | */ 5 | var swfobject=function(){var b="undefined",Q="object",n="Shockwave Flash",p="ShockwaveFlash.ShockwaveFlash",P="application/x-shockwave-flash",m="SWFObjectExprInst",j=window,K=document,T=navigator,o=[],N=[],i=[],d=[],J,Z=null,M=null,l=null,e=false,A=false;var h=function(){var v=typeof K.getElementById!=b&&typeof K.getElementsByTagName!=b&&typeof K.createElement!=b,AC=[0,0,0],x=null;if(typeof T.plugins!=b&&typeof T.plugins[n]==Q){x=T.plugins[n].description;if(x&&!(typeof T.mimeTypes!=b&&T.mimeTypes[P]&&!T.mimeTypes[P].enabledPlugin)){x=x.replace(/^.*\s+(\S+\s+\S+$)/,"$1");AC[0]=parseInt(x.replace(/^(.*)\..*$/,"$1"),10);AC[1]=parseInt(x.replace(/^.*\.(.*)\s.*$/,"$1"),10);AC[2]=/r/.test(x)?parseInt(x.replace(/^.*r(.*)$/,"$1"),10):0}}else{if(typeof j.ActiveXObject!=b){var y=null,AB=false;try{y=new ActiveXObject(p+".7")}catch(t){try{y=new ActiveXObject(p+".6");AC=[6,0,21];y.AllowScriptAccess="always"}catch(t){if(AC[0]==6){AB=true}}if(!AB){try{y=new ActiveXObject(p)}catch(t){}}}if(!AB&&y){try{x=y.GetVariable("$version");if(x){x=x.split(" ")[1].split(",");AC=[parseInt(x[0],10),parseInt(x[1],10),parseInt(x[2],10)]}}catch(t){}}}}var AD=T.userAgent.toLowerCase(),r=T.platform.toLowerCase(),AA=/webkit/.test(AD)?parseFloat(AD.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,q=false,z=r?/win/.test(r):/win/.test(AD),w=r?/mac/.test(r):/mac/.test(AD);/*@cc_on q=true;@if(@_win32)z=true;@elif(@_mac)w=true;@end@*/return{w3cdom:v,pv:AC,webkit:AA,ie:q,win:z,mac:w}}();var L=function(){if(!h.w3cdom){return }f(H);if(h.ie&&h.win){try{K.write(" 4 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | --------------------------------------------------------------------------------