diff --git a/AUTHORS b/AUTHORS
index 151f456f1..27a3e775c 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -13,3 +13,10 @@ Fork author:
Contacts:
sergey.dryabzhinsky@gmail.com
+
+Fork ot the fork author:
+ Raphael Mazelier
+ Paris, France
+
+ Contacts:
+ raph@futomaki.net
diff --git a/DAI.md b/DAI.md
new file mode 100644
index 000000000..db8e18242
--- /dev/null
+++ b/DAI.md
@@ -0,0 +1,32 @@
+
+## AD Insertion markers
+
+nginx-rtmp module can interpret ad insertion markers message sent by encoder in amf rmtp message.
+It then extract the information and write it in an emsg box at the begining of the next chunk.
+The emsg box contains the relative start time of the ad-break, its duration, and possibly some metadata.
+Enabling ad_marker also add the following inband event in the manifest.
+This is the responsability of the player to watch this event, and to treat it.
+
+```
+
+```
+
+Options to enable ad marker processing are :
+
+```
+dash_ad_markers off|on_cuepoint|on_cuepoint_scte35;
+```
+
+ - on_cuepoint is the simple variant (without scte message)
+ - on_cuepoint_scte35 is the scte35 variant with program_id metadata
+
+```
+dash_ad_markers_timehack off|on;
+```
+
+ - off implement the standard timing as described in reference documentation
+ - 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)
+
+Currently there is only elemental encoder tested and compliant.
+
+See [here](https://theyosh.nl/speeltuin/dash/dash.js-2.0.0/samples/ad-insertion/) for the original examples and inspiration.
diff --git a/DRM.md b/DRM.md
new file mode 100644
index 000000000..e07ab44a0
--- /dev/null
+++ b/DRM.md
@@ -0,0 +1,80 @@
+
+## DRM Common Encryption
+
+This fork give the possibility of packaging dash "protected" stream.
+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.
+You can read a brief description here : "https://w3c.github.io/encrypted-media/format-registry/stream/mp4.html#bib-CENC"
+
+### How to use it :
+
+You need at least to enable common_encryption and provide one key and one key id with the following directives :
+
+```
+dash_cenc on; # enable common encryption on all stream in this block
+dash_cenc_kid XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; # 16 bytes KEY-ID in hex
+dash_cenc_key XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; # 16 bytes KEY in hex
+```
+
+It enable automatically Clear-Key pseudo DRM. (use it only for testing purpose)
+
+Currently the are two real DRM supported : Widevine and Microsoft Playready.
+
+For widevine you need the following directives in addition :
+
+```
+dash_wdv on; # enable widevine signalling
+dash_wdv_data AAAAbHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAA... ; # base64 encoded widevine pssh
+```
+
+For playready you need the following directives in addition (you can use both widevine and playready together with the same kid:key pair):
+
+```
+dash_mspr on; # enable playready signalling
+dash_mspr_data AAACsHBzc2gAAAAAmgTweZhAQoarkuZb4Ih...; # base64 encoded playready pssh
+dash_mspr_kid AAATH/7xxxfUbpB8mhqA==; # base64 encoded playready kid
+dash_mspr_pro kAIAAAEAAQCGAjwAVwBSAE0ASABFAEEARA...; # base64 encoded playready PRO (Playready Object)
+```
+
+### Implementation :
+
+_TLDR;_ This was quite an adventure
+
+The implementation is based on the ISO_IEC_23001-7_2016 normative document.
+I also took lot of inspiration on kaltura nginx-vod module.
+
+It implement the minimal requirement of the norm, the 'cenc' scheme, AES-CTR mode full sample and video NAL Subsample encryption.
+
+Audio track are encrypted in full sample mode with AES-CTR.
+
+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).
+
+The clear size is rounded to make encrypted size of data a multiple of the AES-CRT block size.
+
+The implementation allow only one KID:KEY couple used for all tracks.
+
+The implementation use 64bits IVs.
+
+### Conformity :
+
+This implementation have been tested with and known working :
+
+Clearkey :
+ - Firefox: dashjs/shakaplayer
+ - Chrome: dashjs/shakaplayer
+
+Widevine :
+- Firefox: dashjs/shakaplayer
+- Chrome: dashjs/shakaplayer
+
+Playready:
+ - Edge : dashjs/shakaplayer
+
+Bitmovin player seem also to work.
+
+### Thanks:
+
+- Thanks to all the opensource communauty.
+- Special thanks to Eran Kornblau from Kaltura, Joey Parrish, and Jacob Timble.
+
+
+
diff --git a/README.md b/README.md
index d53d0e6b9..effa4434c 100644
--- a/README.md
+++ b/README.md
@@ -1,336 +1,76 @@
# NGINX-based Media Streaming Server
-## nginx-rtmp-module
-
-### Project blog
-
- http://nginx-rtmp.blogspot.com
-
-### Documentation
-
-* [Home](doc/README.md)
-* [Control module](doc/control_modul.md)
-* [Debug log](doc/debug_log.md)
-* [Directives](doc/directives.md)
-* [Examples](doc/examples.md)
-* [Exec wrapper in bash](doc/exec_wrapper_in_bash.md)
-* [FAQ](doc/faq.md)
-* [Getting number of subscribers](doc/getting_number_of_subscribers.md)
-* [Getting started with nginx rtmp](doc/getting_started.md)
-* [Installing in Gentoo](doc/installing_in_gentoo.md)
-* [Installing on Ubuntu using PPAs](doc/installing_ubuntu_using_ppas.md)
-* [Tutorial](doc/tutorial.md)
-
-*Source: https://github.com/arut/nginx-rtmp-module/wiki*
-
-* [Latest updates](doc/README.md#updates)
-
-### Google group
-
- https://groups.google.com/group/nginx-rtmp
-
- https://groups.google.com/group/nginx-rtmp-ru (Russian)
-
-### Donation page (Paypal etc)
-
- http://arut.github.com/nginx-rtmp-module/
-
-### Features
-
-* RTMP/HLS/MPEG-DASH live streaming
-
-* RTMP Video on demand FLV/MP4,
- playing from local filesystem or HTTP
-
-* Stream relay support for distributed
- streaming: push & pull models
-
-* Recording streams in multiple FLVs
-
-* H264/AAC support
-
-* Online transcoding with FFmpeg
-
-* HTTP callbacks (publish/play/record/update etc)
-
-* Running external programs on certain events (exec)
-
-* HTTP control module for recording audio/video and dropping clients
-
-* Advanced buffering techniques
- to keep memory allocations at a minimum
- level for faster streaming and low
- memory footprint
-
-* Proved to work with Wirecast, FMS, Wowza,
- JWPlayer, FlowPlayer, StrobeMediaPlayback,
- ffmpeg, avconv, rtmpdump, flvstreamer
- and many more
-
-* Statistics in XML/XSL in machine- & human-
- readable form
-
-* Linux/FreeBSD/MacOS/Windows
-
-### Build
-
-cd to NGINX source directory & run this:
-
- ./configure --add-module=/path/to/nginx-rtmp-module
- make
- make install
-
-Several versions of nginx (1.3.14 - 1.5.0) require http_ssl_module to be
-added as well:
-
- ./configure --add-module=/path/to/nginx-rtmp-module --with-http_ssl_module
-
-For building debug version of nginx add `--with-debug`
-
- ./configure --add-module=/path/to-nginx/rtmp-module --with-debug
-
-[Read more about debug log](https://github.com/arut/nginx-rtmp-module/wiki/Debug-log)
-
-### Contributing and Branch Policy
-
-The "dev" branch is the one where all contributions will be merged before reaching "master".
-If you plan to propose a patch, please commit into the "dev" branch or its own feature branch.
-Direct commit to "master" are not permitted.
-
-### Windows limitations
-
-Windows support is limited. These features are not supported
-
-* execs
-* static pulls
-* auto_push
-
-### RTMP URL format
-
- rtmp://rtmp.example.com/app[/name]
-
-app - should match one of application {}
- blocks in config
-
-name - interpreted by each application
- can be empty
-
-
-### Multi-worker live streaming
-
-This NGINX-RTMP module does not support multi-worker live
-streaming. While this feature can be enabled through rtmp_auto_push on|off directive, it is ill advised because it is incompatible with NGINX versions starting 1.7.2 and up, there for it should not be used.
-
-
-### Example nginx.conf
-
- rtmp {
-
- server {
-
- listen 1935;
-
- chunk_size 4000;
-
- # TV mode: one publisher, many subscribers
- application mytv {
-
- # enable live streaming
- live on;
-
- # record first 1K of stream
- record all;
- record_path /tmp/av;
- record_max_size 1K;
-
- # append current timestamp to each flv
- record_unique on;
-
- # publish only from localhost
- allow publish 127.0.0.1;
- deny publish all;
-
- #allow play all;
- }
-
- # Transcoding (ffmpeg needed)
- application big {
- live on;
-
- # On every pusblished stream run this command (ffmpeg)
- # with substitutions: $app/${app}, $name/${name} for application & stream name.
- #
- # This ffmpeg call receives stream from this application &
- # reduces the resolution down to 32x32. The stream is the published to
- # 'small' application (see below) under the same name.
- #
- # ffmpeg can do anything with the stream like video/audio
- # transcoding, resizing, altering container/codec params etc
- #
- # Multiple exec lines can be specified.
-
- exec ffmpeg -re -i rtmp://localhost:1935/$app/$name -vcodec flv -acodec copy -s 32x32
- -f flv rtmp://localhost:1935/small/${name};
- }
-
- application small {
- live on;
- # Video with reduced resolution comes here from ffmpeg
- }
-
- application webcam {
- live on;
-
- # Stream from local webcam
- exec_static ffmpeg -f video4linux2 -i /dev/video0 -c:v libx264 -an
- -f flv rtmp://localhost:1935/webcam/mystream;
- }
-
- application mypush {
- live on;
-
- # Every stream published here
- # is automatically pushed to
- # these two machines
- push rtmp1.example.com;
- push rtmp2.example.com:1934;
- }
-
- application mypull {
- live on;
-
- # Pull all streams from remote machine
- # and play locally
- pull rtmp://rtmp3.example.com pageUrl=www.example.com/index.html;
- }
-
- application mystaticpull {
- live on;
-
- # Static pull is started at nginx start
- pull rtmp://rtmp4.example.com pageUrl=www.example.com/index.html name=mystream static;
- }
-
- # video on demand
- application vod {
- play /var/flvs;
- }
-
- application vod2 {
- play /var/mp4s;
- }
-
- # Many publishers, many subscribers
- # no checks, no recording
- application videochat {
-
- live on;
-
- # The following notifications receive all
- # the session variables as well as
- # particular call arguments in HTTP POST
- # request
-
- # Make HTTP request & use HTTP retcode
- # to decide whether to allow publishing
- # from this connection or not
- on_publish http://localhost:8080/publish;
-
- # Same with playing
- on_play http://localhost:8080/play;
-
- # Publish/play end (repeats on disconnect)
- on_done http://localhost:8080/done;
-
- # All above mentioned notifications receive
- # standard connect() arguments as well as
- # play/publish ones. If any arguments are sent
- # with GET-style syntax to play & publish
- # these are also included.
- # Example URL:
- # rtmp://localhost/myapp/mystream?a=b&c=d
-
- # record 10 video keyframes (no audio) every 2 minutes
- record keyframes;
- record_path /tmp/vc;
- record_max_frames 10;
- record_interval 2m;
-
- # Async notify about an flv recorded
- on_record_done http://localhost:8080/record_done;
-
- }
-
-
- # HLS
-
- # For HLS to work please create a directory in tmpfs (/tmp/hls here)
- # for the fragments. The directory contents is served via HTTP (see
- # http{} section in config)
- #
- # Incoming stream must be in H264/AAC. For iPhones use baseline H264
- # profile (see ffmpeg example).
- # This example creates RTMP stream from movie ready for HLS:
- #
- # ffmpeg -loglevel verbose -re -i movie.avi -vcodec libx264
- # -vprofile baseline -acodec libmp3lame -ar 44100 -ac 1
- # -f flv rtmp://localhost:1935/hls/movie
- #
- # If you need to transcode live stream use 'exec' feature.
- #
- application hls {
- live on;
- hls on;
- hls_path /tmp/hls;
- }
-
- # MPEG-DASH is similar to HLS
-
- application dash {
- live on;
- dash on;
- dash_path /tmp/dash;
- }
- }
+## nginx-rtmp-module (dash enhanced version)
+
+Forked from https://github.com/sergey-dryabzhinsky/ which was the most up to date version (until now)
+
+Notable new features :
+
+ - add the possibility to make adaptative streaming (show below configuration, using ffmpeg to trancode in 3 variants, and produce one manifest).
+ note the "max" flag which indicate which representation should have max witdh and height and so use it to create the variant manifest.
+ you can also use any encoder to directly push the variant.
+ - add the support of using repetition in manifest to shorten them (option dash_repetition) (thanks to Streamroot)
+ - add the support of common-encryption; currently working DRM are ClearKey/Widevine/Playready (see specific doc [here](DRM.md))
+ - 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))
+
+
+See original doc here for full list of options.
+
+```
+ rtmp {
+ server {
+ listen 1935;
+
+ application ingest {
+ live on;
+ exec /usr/bin/ffmpeg -i rtmp://localhost/$app/$name \
+ -c:a libfdk_aac -b:a 64k -c:v libx264 -preset fast -profile:v baseline -vsync cfr -s 1024x576 -b:v 1024K -bufsize 1024k \
+ -f flv rtmp://localhost/dash/$name_hi \
+ -c:a libfdk_aac -b:a 64k -c:v libx264 -preset fast -profile:v baseline -vsync cfr -s 640x360 -b:v 832K -bufsize 832k \
+ -f flv rtmp://localhost/dash/$name_med \
+ -c:a libfdk_aac -b:a 64k -c:v libx264 -preset fast -profile:v baseline -vsync cfr -s 320x180 -b:v 256K -bufsize 256k \
+ -f flv rtmp://localhost/dash/$name_low
}
+
+ application dash {
+ live on;
+ dash on;
+ dash_nested on;
+ dash_repetition on;
+ dash_path /dev/shm/dash;
+ dash_fragment 4; # 4 second is generaly a good choice for live
+ dash_playlist_length 120; # keep 120s of tail
+ dash_cleanup on;
+ dash_variant _low bandwidth="256000" width="320" height="180";
+ dash_variant _med bandwidth="832000" width="640" height="360";
+ dash_variant _hi bandwidth="1024000" width="1024" height="576" max;
+ }
+ }
+
+ server {
+ listen 443 ssl;
+ location / {
+ root /var/www;
+ add_header Cache-Control no-cache;
+ add_header 'Access-Control-Allow-Origin' '*';
+ }
+ location /dash/live/index.mpd {
+ alias /dev/shm/dash/live/index.mpd;
+ add_header 'Access-Control-Allow-Origin' '*';
+ add_header Cache-Control 'public, max-age=0, s-maxage=2';
+ }
+ location /dash/live {
+ alias /dev/shm/dash/live;
+ add_header 'Access-Control-Allow-Origin' '*';
+ add_header Cache-Control 'public, max-age=600, s-maxage=600';
+ }
+
+ server_name live.site.net;
+ ssl_certificate /etc/letsencrypt/live/live.site.net/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/live.sit.net/privkey.pem;
- # HTTP can be used for accessing RTMP stats
- http {
-
- server {
-
- listen 8080;
-
- # This URL provides RTMP statistics in XML
- location /stat {
- rtmp_stat all;
-
- # Use this stylesheet to view XML as web page
- # in browser
- rtmp_stat_stylesheet stat.xsl;
- }
-
- location /stat.xsl {
- # XML stylesheet to view RTMP stats.
- # Copy stat.xsl wherever you want
- # and put the full directory path here
- root /path/to/stat.xsl/;
- }
+ }
+}
+```
- location /hls {
- # Serve HLS fragments
- types {
- application/vnd.apple.mpegurl m3u8;
- video/mp2t ts;
- }
- root /tmp;
- add_header Cache-Control no-cache;
- }
- location /dash {
- # Serve DASH fragments
- root /tmp;
- add_header Cache-Control no-cache;
- }
- }
- }
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 000000000..6dd0b18b5
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,23 @@
+## TODO
+
+- doc // OK
+- need re-upstream // wait response
+- rewritte the variant code for dash, using memory ?
+
+- add option emsg_timestamp_hack
+
+
+- test common encryption code (OK clearkey, Widevine both on chrome/firefox!)
+- need to write some doc about cenc/drm implem
+- refacto code for writing content protection in manifest // OK
+- clarify the use of %V in ngx_printf // OK
+- refacto code for init / kid // OK
+- correct pssh in manifest // OK
+- add pssh / cenc in variant mpd // OK
+- add real base64 encrypt pssh data // OK
+- add support for sub sample encryption //OK
+- add sig for wdv //OK
+- add struct for drm info // OK
+- add pssh in init file for wdv // OK
+- add msplayready support // OK need pssh in init file
+
diff --git a/config b/config
index 13f00e83c..32f6d551e 100644
--- a/config
+++ b/config
@@ -43,6 +43,8 @@ RTMP_DEPS=" \
$ngx_addon_dir/ngx_rtmp_proxy_protocol.h \
$ngx_addon_dir/hls/ngx_rtmp_mpegts.h \
$ngx_addon_dir/dash/ngx_rtmp_mp4.h \
+ $ngx_addon_dir/dash/ngx_rtmp_cenc.h \
+ $ngx_addon_dir/dash/ngx_rtmp_dash_templates.h \
"
RTMP_CORE_SRCS=" \
$ngx_addon_dir/ngx_rtmp.c \
@@ -78,6 +80,7 @@ RTMP_CORE_SRCS=" \
$ngx_addon_dir/hls/ngx_rtmp_mpegts.c \
$ngx_addon_dir/hls/ngx_rtmp_mpegts_crc.c \
$ngx_addon_dir/dash/ngx_rtmp_mp4.c \
+ $ngx_addon_dir/dash/ngx_rtmp_cenc.c \
"
RTMP_HTTP_SRCS=" \
$ngx_addon_dir/ngx_rtmp_stat_module.c \
diff --git a/dash/ngx_rtmp_cenc.c b/dash/ngx_rtmp_cenc.c
new file mode 100644
index 000000000..e4af2b177
--- /dev/null
+++ b/dash/ngx_rtmp_cenc.c
@@ -0,0 +1,190 @@
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "ngx_rtmp_cenc.h"
+
+
+void
+debug_counter(ngx_rtmp_session_t *s, uint8_t *c, uint8_t *k, size_t l)
+{
+ u_char hexc[AES_BLOCK_SIZE*2+1];
+ u_char hexk[AES_BLOCK_SIZE*2+1];
+
+ ngx_hex_dump(hexc, c, AES_BLOCK_SIZE);
+ ngx_hex_dump(hexk, k, AES_BLOCK_SIZE);
+ hexc[AES_BLOCK_SIZE*2] = '\0';
+ hexk[AES_BLOCK_SIZE*2] = '\0';
+
+ ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ "dash cenc_counter: %ui %s %s", l, hexc, hexk);
+}
+
+
+ngx_int_t
+ngx_rtmp_cenc_read_hex(ngx_str_t src, u_char* dst)
+{
+ u_char l, h;
+ size_t i;
+
+ if (src.len != NGX_RTMP_CENC_KEY_SIZE*2) {
+ return NGX_ERROR;
+ }
+
+ for (i = 0; i < NGX_RTMP_CENC_KEY_SIZE; i++) {
+ l = ngx_tolower(src.data[i*2]);
+ l = l >= 'a' ? l - 'a' + 10 : l - '0';
+ h = ngx_tolower(src.data[i*2+1]);
+ h = h >= 'a' ? h - 'a' + 10 : h - '0';
+ dst[i] = (l << 4) | h;
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_rtmp_cenc_rand_iv(u_char* iv)
+{
+ if(RAND_bytes(iv, NGX_RTMP_CENC_IV_SIZE) != 1) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+void
+ngx_rtmp_cenc_increment_iv(u_char* iv)
+{
+ int i;
+
+ for (i = NGX_RTMP_CENC_IV_SIZE - 1; i >= 0; i--) {
+ iv[i]++;
+ if (iv[i])
+ break;
+ }
+}
+
+
+ngx_int_t
+ngx_rtmp_cenc_aes_ctr_encrypt(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv,
+ uint8_t *data, size_t data_len)
+{
+ /* aes-ctr implementation */
+
+ EVP_CIPHER_CTX* ctx;
+ size_t j, len, left = data_len;
+ int i, w;
+ uint8_t *pos = data;
+ uint8_t counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE];
+
+ ngx_memset(counter + NGX_RTMP_CENC_IV_SIZE, 0, NGX_RTMP_CENC_IV_SIZE);
+ ngx_memcpy(counter, iv, NGX_RTMP_CENC_IV_SIZE);
+
+ ctx = EVP_CIPHER_CTX_new();
+ if (ctx == NULL) {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "dash rtmp_cenc_encrypt: evp_cipher_ctx failed");
+ return NGX_ERROR;
+ }
+
+ if (EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, NULL) != 1) {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "dash rtmp_cenc_encrypt: evp_encrypt_init failed");
+ return NGX_ERROR;
+ }
+
+ while (left > 0) {
+
+ if (EVP_EncryptUpdate(ctx, buf, &w, counter, AES_BLOCK_SIZE) != 1) {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "dash rtmp_cenc_encrypt: evp_encrypt_update failed");
+ return NGX_ERROR;
+ }
+
+ len = (left < AES_BLOCK_SIZE) ? left : AES_BLOCK_SIZE;
+ for (j = 0; j < len; j++)
+ pos[j] ^= buf[j];
+ pos += len;
+ left -= len;
+
+ for (i = AES_BLOCK_SIZE - 1; i >= 0; i--) {
+ counter[i]++;
+ if (counter[i])
+ break;
+ }
+ }
+
+ EVP_CIPHER_CTX_free(ctx);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_rtmp_cenc_encrypt_full_sample(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv,
+ uint8_t *data, size_t data_len)
+{
+ return ngx_rtmp_cenc_aes_ctr_encrypt(s, key, iv, data, data_len);
+}
+
+
+ngx_int_t
+ngx_rtmp_cenc_encrypt_sub_sample(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv,
+ uint8_t *data, size_t data_len, size_t *clear_data_len)
+{
+ size_t crypted_data_len;
+
+ /* small sample : leave it in clear */
+ if (data_len <= NGX_RTMP_CENC_MIN_CLEAR_SIZE) {
+ *clear_data_len = data_len;
+ return NGX_OK;
+ }
+
+ /* skip sufficient amount of data to leave nalu header/infos
+ * in clear to conform to the norm */
+ crypted_data_len =
+ ((data_len - NGX_RTMP_CENC_MIN_CLEAR_SIZE) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
+ *clear_data_len = data_len - crypted_data_len;
+
+ data += *clear_data_len;
+ return ngx_rtmp_cenc_aes_ctr_encrypt(s, key, iv, data, crypted_data_len);
+
+}
+
+
+ngx_int_t
+ngx_rtmp_cenc_content_protection_pssh(u_char* kid, ngx_str_t *dest_pssh)
+{
+ ngx_str_t src_pssh;
+ u_char dest[NGX_RTMP_CENC_MAX_PSSH_SIZE];
+
+ u_char pssh[] = {
+ 0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68, // pssh box header
+ 0x01, 0x00, 0x00, 0x00, // header
+ 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // systemID
+ 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+ 0x00, 0x00, 0x00, 0x01, // kid count
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // kid
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00 // data size
+ };
+
+ ngx_memcpy(pssh+32, kid, NGX_RTMP_CENC_KEY_SIZE);
+
+ src_pssh.len = sizeof(pssh);
+ src_pssh.data = pssh;
+
+ dest_pssh->len = ngx_base64_encoded_length(src_pssh.len);
+ dest_pssh->data = dest;
+
+ ngx_encode_base64(dest_pssh, &src_pssh);
+
+ return NGX_OK;
+}
+
diff --git a/dash/ngx_rtmp_cenc.h b/dash/ngx_rtmp_cenc.h
new file mode 100644
index 000000000..a04c002e7
--- /dev/null
+++ b/dash/ngx_rtmp_cenc.h
@@ -0,0 +1,44 @@
+#ifndef _NGX_RTMP_CENC_H_INCLUDED_
+#define _NGX_RTMP_CENC_H_INCLUDED_
+
+
+#define NGX_RTMP_CENC_IV_SIZE (8)
+#define NGX_RTMP_CENC_KEY_SIZE (16)
+#define NGX_RTMP_CENC_MIN_CLEAR_SIZE (100)
+#define NGX_RTMP_CENC_MAX_PSSH_SIZE (1024)
+
+
+typedef struct {
+ u_char kid[NGX_RTMP_CENC_KEY_SIZE];
+ unsigned wdv:1;
+ ngx_str_t wdv_data;
+ unsigned mspr:1;
+ ngx_str_t mspr_data;
+ ngx_str_t mspr_kid;
+ ngx_str_t mspr_pro;
+} ngx_rtmp_cenc_drm_info_t;
+
+
+ngx_int_t
+ngx_rtmp_cenc_read_hex(ngx_str_t src, u_char* dst);
+
+ngx_int_t
+ngx_rtmp_cenc_rand_iv(u_char* iv);
+
+void
+ngx_rtmp_cenc_increment_iv(u_char* iv);
+
+ngx_int_t
+ngx_rtmp_cenc_encrypt_full_sample(ngx_rtmp_session_t *s,
+ uint8_t *key, uint8_t *iv, uint8_t *data, size_t data_len);
+
+ngx_int_t
+ngx_rtmp_cenc_encrypt_sub_sample(ngx_rtmp_session_t *s,
+ uint8_t *key, uint8_t *iv, uint8_t *data,
+ size_t data_len, size_t *clear_data_len);
+
+ngx_int_t
+ngx_rtmp_cenc_content_protection_pssh(u_char* kid,
+ ngx_str_t *dest_pssh);
+
+#endif /* _NGX_RTMP_CENC_H_INCLUDED_ */
diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c
index 1038ae285..99a21360b 100644
--- a/dash/ngx_rtmp_dash_module.c
+++ b/dash/ngx_rtmp_dash_module.c
@@ -6,6 +6,8 @@
#include
#include "ngx_rtmp_live_module.h"
#include "ngx_rtmp_mp4.h"
+#include "ngx_rtmp_dash_templates.h"
+#include "ngx_rtmp_cenc.h"
static ngx_rtmp_publish_pt next_publish;
@@ -15,11 +17,14 @@ static ngx_rtmp_stream_eof_pt next_stream_eof;
static ngx_rtmp_playlist_pt next_playlist;
+static char * ngx_rtmp_dash_variant(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
static ngx_int_t ngx_rtmp_dash_postconfiguration(ngx_conf_t *cf);
static void * ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf);
static char * ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf,
void *parent, void *child);
-static ngx_int_t ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s);
+static ngx_int_t ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s,
+ ngx_rtmp_cenc_drm_info_t *drmi);
static ngx_int_t ngx_rtmp_dash_ensure_directory(ngx_rtmp_session_t *s);
@@ -31,6 +36,7 @@ static ngx_int_t ngx_rtmp_dash_ensure_directory(ngx_rtmp_session_t *s);
#define NGX_RTMP_DASH_GMT_LENGTH sizeof("1970-09-28T12:00:00+06:00")
typedef struct {
+ uint64_t u_timestamp;
uint32_t timestamp;
uint32_t duration;
} ngx_rtmp_dash_frag_t;
@@ -46,14 +52,28 @@ typedef struct {
char type;
uint32_t earliest_pres_time;
uint32_t latest_pres_time;
+ unsigned is_protected:1;
+ u_char key[NGX_RTMP_CENC_KEY_SIZE];
+ u_char iv[NGX_RTMP_CENC_IV_SIZE];
ngx_rtmp_mp4_sample_t samples[NGX_RTMP_DASH_MAX_SAMPLES];
} ngx_rtmp_dash_track_t;
typedef struct {
+ ngx_str_t suffix;
+ ngx_array_t args;
+} ngx_rtmp_dash_variant_t;
+
+
+typedef struct {
+ ngx_str_t segments;
+ ngx_str_t segments_bak;
ngx_str_t playlist;
ngx_str_t playlist_bak;
+ ngx_str_t var_playlist;
+ ngx_str_t var_playlist_bak;
ngx_str_t name;
+ ngx_str_t varname;
ngx_str_t stream;
ngx_time_t start_time;
@@ -64,6 +84,13 @@ typedef struct {
unsigned opened:1;
unsigned has_video:1;
unsigned has_audio:1;
+ unsigned start_cuepoint:1;
+ unsigned end_cuepoint:1;
+
+ uint32_t cuepoint_starttime;
+ uint32_t cuepoint_endtime;
+ uint32_t cuepoint_duration;
+ uint32_t cuepoint_id;
ngx_file_t video_file;
ngx_file_t audio_file;
@@ -72,6 +99,10 @@ typedef struct {
ngx_rtmp_dash_track_t audio;
ngx_rtmp_dash_track_t video;
+ ngx_rtmp_dash_variant_t *var;
+
+ ngx_rtmp_cenc_drm_info_t drm_info;
+
} ngx_rtmp_dash_ctx_t;
@@ -94,11 +125,32 @@ static ngx_conf_enum_t ngx_rtmp_dash_clock_compensation_type_sl
{ ngx_null_string, 0 }
};
+#define NGX_RTMP_DASH_AD_MARKERS_OFF 1
+#define NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT 2
+#define NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35 3
+
+static ngx_conf_enum_t ngx_rtmp_dash_ad_markers_type_slots[] = {
+ { ngx_string("off"), NGX_RTMP_DASH_AD_MARKERS_OFF },
+ { ngx_string("on_cuepoint"), NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT },
+ { ngx_string("on_cuepoint_scte35"), NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35 },
+ { ngx_null_string, 0 }
+};
+
typedef struct {
ngx_flag_t dash;
ngx_msec_t fraglen;
ngx_msec_t playlen;
ngx_flag_t nested;
+ ngx_flag_t cenc;
+ ngx_str_t cenc_key;
+ ngx_str_t cenc_kid;
+ ngx_flag_t wdv;
+ ngx_str_t wdv_data;
+ ngx_flag_t mspr;
+ ngx_str_t mspr_data;
+ ngx_str_t mspr_kid;
+ ngx_str_t mspr_pro;
+ ngx_flag_t repetition;
ngx_uint_t clock_compensation; // Try to compensate clock drift
// between client and server (on client side)
ngx_str_t clock_helper_uri; // Use uri to static file on HTTP server
@@ -108,6 +160,9 @@ typedef struct {
ngx_uint_t winfrags;
ngx_flag_t cleanup;
ngx_path_t *slot;
+ ngx_array_t *variant;
+ ngx_uint_t ad_markers;
+ ngx_flag_t ad_markers_timehack;
} ngx_rtmp_dash_app_conf_t;
@@ -155,6 +210,76 @@ static ngx_command_t ngx_rtmp_dash_commands[] = {
offsetof(ngx_rtmp_dash_app_conf_t, nested),
NULL },
+ { ngx_string("dash_repetition"),
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_flag_slot,
+ NGX_RTMP_APP_CONF_OFFSET,
+ offsetof(ngx_rtmp_dash_app_conf_t, repetition),
+ NULL },
+
+ { ngx_string("dash_cenc"),
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_flag_slot,
+ NGX_RTMP_APP_CONF_OFFSET,
+ offsetof(ngx_rtmp_dash_app_conf_t, cenc),
+ NULL },
+
+ { ngx_string("dash_cenc_key"),
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_RTMP_APP_CONF_OFFSET,
+ offsetof(ngx_rtmp_dash_app_conf_t, cenc_key),
+ NULL },
+
+ { ngx_string("dash_cenc_kid"),
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_RTMP_APP_CONF_OFFSET,
+ offsetof(ngx_rtmp_dash_app_conf_t, cenc_kid),
+ NULL },
+
+ { ngx_string("dash_wdv"),
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_flag_slot,
+ NGX_RTMP_APP_CONF_OFFSET,
+ offsetof(ngx_rtmp_dash_app_conf_t, wdv),
+ NULL },
+
+ { ngx_string("dash_wdv_data"),
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_RTMP_APP_CONF_OFFSET,
+ offsetof(ngx_rtmp_dash_app_conf_t, wdv_data),
+ NULL },
+
+ { ngx_string("dash_mspr"),
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_flag_slot,
+ NGX_RTMP_APP_CONF_OFFSET,
+ offsetof(ngx_rtmp_dash_app_conf_t, mspr),
+ NULL },
+
+ { ngx_string("dash_mspr_data"),
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_RTMP_APP_CONF_OFFSET,
+ offsetof(ngx_rtmp_dash_app_conf_t, mspr_data),
+ NULL },
+
+ { ngx_string("dash_mspr_kid"),
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_RTMP_APP_CONF_OFFSET,
+ offsetof(ngx_rtmp_dash_app_conf_t, mspr_kid),
+ NULL },
+
+ { ngx_string("dash_mspr_pro"),
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_RTMP_APP_CONF_OFFSET,
+ offsetof(ngx_rtmp_dash_app_conf_t, mspr_pro),
+ NULL },
+
{ ngx_string("dash_clock_compensation"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
ngx_conf_set_enum_slot,
@@ -169,6 +294,27 @@ static ngx_command_t ngx_rtmp_dash_commands[] = {
offsetof(ngx_rtmp_dash_app_conf_t, clock_helper_uri),
NULL },
+ { ngx_string("dash_variant"),
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_1MORE,
+ ngx_rtmp_dash_variant,
+ NGX_RTMP_APP_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("dash_ad_markers"),
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_enum_slot,
+ NGX_RTMP_APP_CONF_OFFSET,
+ offsetof(ngx_rtmp_dash_app_conf_t, ad_markers),
+ &ngx_rtmp_dash_ad_markers_type_slots },
+
+ { ngx_string("dash_ad_markers_timehack"),
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_flag_slot,
+ NGX_RTMP_APP_CONF_OFFSET,
+ offsetof(ngx_rtmp_dash_app_conf_t, ad_markers_timehack),
+ NULL },
+
ngx_null_command
};
@@ -254,12 +400,436 @@ ngx_rtmp_dash_gcd(ngx_uint_t m, ngx_uint_t n)
ngx_uint_t temp;
- while (n) {
- temp=n;
- n=m % n;
- m=temp;
+ while (n) {
+ temp=n;
+ n=m % n;
+ m=temp;
+ }
+ return m;
+}
+
+
+static u_char *
+ngx_rtmp_dash_write_segment(u_char *p, u_char *last, ngx_uint_t t,
+ ngx_uint_t d, ngx_uint_t r)
+{
+ if (r == 0) {
+ p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME, t, d);
+ } else {
+ p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME_WITH_REPETITION, t, d, r);
+ }
+
+ return p;
+}
+
+
+static u_char *
+ngx_rtmp_dash_write_segment_timeline(ngx_rtmp_session_t *s, ngx_rtmp_dash_ctx_t *ctx,
+ ngx_rtmp_dash_app_conf_t *dacf, u_char *p, u_char *last)
+{
+ ngx_uint_t i, t, d, r;
+ ngx_rtmp_dash_frag_t *f;
+
+ for (i = 0; i < ctx->nfrags; i++) {
+ f = ngx_rtmp_dash_get_frag(s, i);
+
+ if (dacf->repetition) {
+ if (i == 0) {
+ t = f->timestamp;
+ d = f->duration;
+ r = 0;
+ } else {
+ if (f->duration == d) {
+ r++;
+ } else {
+ p = ngx_rtmp_dash_write_segment(p, last, t, d, r);
+ t = f->timestamp;
+ d = f->duration;
+ r = 0;
+ }
+ }
+ if (i == ctx->nfrags - 1) {
+ p = ngx_rtmp_dash_write_segment(p, last, t, d, r);
+ }
+ } else {
+ //p = ngx_rtmp_dash_write_segment(p, last, f->u_timestamp, f->duration, 0);
+ p = ngx_rtmp_dash_write_segment(p, last, f->timestamp, f->duration, 0);
+ }
+ }
+
+ return p;
+}
+
+
+static u_char *
+ngx_rtmp_dash_write_content_protection(ngx_rtmp_session_t *s,
+ ngx_rtmp_cenc_drm_info_t *drmi, u_char *p, u_char *last)
+{
+ u_char *k;
+ ngx_str_t cenc_pssh;
+
+ k = drmi->kid;
+
+ ngx_rtmp_cenc_content_protection_pssh(k, &cenc_pssh);
+
+ p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_CENC,
+ k[0], k[1], k[2], k[3],
+ k[4], k[5], k[6], k[7],
+ k[8], k[9], k[10], k[11], k[12], k[13], k[14], k[15]);
+
+
+ p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC,
+ &cenc_pssh);
+
+ if (drmi->wdv) {
+ p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_WDV,
+ &drmi->wdv_data);
+ }
+
+ if (drmi->mspr) {
+ p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_MSPR,
+ &drmi->mspr_data,
+ &drmi->mspr_kid,
+ &drmi->mspr_pro);
+ }
+
+ return p;
+}
+
+
+static ngx_int_t
+ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s)
+{
+ char *sep;
+ u_char *p, *last;
+ ssize_t n;
+ ngx_fd_t fd, fds;
+ struct tm tm;
+ ngx_uint_t i, j, k, frame_rate_num, frame_rate_denom;
+ ngx_uint_t depth_msec, depth_sec;
+ ngx_uint_t update_period, update_period_msec;
+ ngx_uint_t start_time, buffer_time, buffer_time_msec;
+ ngx_uint_t presentation_delay, presentation_delay_msec;
+ ngx_uint_t gcd, par_x, par_y;
+ ngx_rtmp_dash_ctx_t *ctx;
+ ngx_rtmp_codec_ctx_t *codec_ctx;
+ ngx_rtmp_dash_frag_t *f;
+ ngx_rtmp_dash_app_conf_t *dacf;
+ ngx_rtmp_dash_variant_t *var;
+ ngx_str_t *arg;
+
+ ngx_rtmp_playlist_t v;
+
+ static u_char buffer[NGX_RTMP_DASH_BUFSIZE];
+ static u_char available_time[NGX_RTMP_DASH_GMT_LENGTH];
+ static u_char publish_time[NGX_RTMP_DASH_GMT_LENGTH];
+ static u_char buffer_depth[sizeof("P00Y00M00DT00H00M00.000S")];
+ static u_char frame_rate[(NGX_INT_T_LEN * 2) + 2];
+ static u_char seg_path[NGX_MAX_PATH + 1];
+
+ dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module);
+ ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module);
+ codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
+
+ if (dacf == NULL || ctx == NULL || codec_ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ fd = ngx_open_file(ctx->var_playlist_bak.data, NGX_FILE_WRONLY,
+ NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS);
+
+ if (fd == NGX_INVALID_FILE) {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
+ "dash: open failed: '%V'", &ctx->var_playlist_bak);
+ return NGX_ERROR;
+ }
+
+ /* availabity and publish time should be relative to peer epoch */
+ start_time = ctx->start_time.sec - (s->peer_epoch/1000);
+ ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ "Fixing start_time=%uD %uD epoch=%uD new_start_time=%uD",
+ (uint32_t)ctx->start_time.sec, (uint32_t)ctx->start_time.msec,
+ (uint32_t)s->peer_epoch,
+ (uint32_t)start_time);
+
+ /**
+ * Availability time must be equal stream start time
+ * Cos segments time counting from it
+ */
+ ngx_libc_gmtime(start_time, &tm);
+
+ *ngx_sprintf(available_time, "%4d-%02d-%02dT%02d:%02d:%02dZ",
+ tm.tm_year + 1900, tm.tm_mon + 1,
+ tm.tm_mday, tm.tm_hour,
+ tm.tm_min, tm.tm_sec
+ ) = 0;
+ /*
+ *ngx_sprintf(available_time, "%4d-%02d-%02dT%02d:%02d:%02dZ",
+ 1970, 1,
+ 1, 0,
+ 0, 0
+ ) = 0;
+ */
+
+ /* Stream publish time */
+ *ngx_sprintf(publish_time, "%s", available_time) = 0;
+
+ depth_sec = (ngx_uint_t) (
+ ngx_rtmp_dash_get_frag(s, ctx->nfrags - 1)->timestamp +
+ ngx_rtmp_dash_get_frag(s, ctx->nfrags - 1)->duration -
+ ngx_rtmp_dash_get_frag(s, 0)->timestamp);
+
+ depth_msec = depth_sec % 1000;
+ depth_sec -= depth_msec;
+ depth_sec /= 1000;
+
+ ngx_libc_gmtime(depth_sec, &tm);
+
+ *ngx_sprintf(buffer_depth, "P%dY%02dM%02dDT%dH%02dM%02d.%03dS",
+ tm.tm_year - 70, tm.tm_mon,
+ tm.tm_mday - 1, tm.tm_hour,
+ tm.tm_min, tm.tm_sec,
+ depth_msec) = 0;
+
+ last = buffer + sizeof(buffer);
+
+ /**
+ * Calculate playlist minimal update period
+ * This should be more than biggest segment duration
+ * Cos segments rounded by keyframe/GOP.
+ * And that time not always equals to fragment length.
+ */
+ update_period = dacf->fraglen;
+
+ for (i = 0; i < ctx->nfrags; i++) {
+ f = ngx_rtmp_dash_get_frag(s, i);
+ if (f->duration > update_period) {
+ update_period = f->duration;
+ }
+ }
+
+ // Reasonable delay for streaming
+ presentation_delay = update_period * 2 + 1000;
+ presentation_delay_msec = presentation_delay % 1000;
+ presentation_delay -= presentation_delay_msec;
+ presentation_delay /= 1000;
+
+ // Calculate msec part and seconds
+ update_period_msec = update_period % 1000;
+ update_period -= update_period_msec;
+ update_period /= 1000;
+
+ // Buffer length by default fragment length
+ buffer_time = dacf->fraglen;
+ buffer_time_msec = buffer_time % 1000;
+ buffer_time -= buffer_time_msec;
+ buffer_time /= 1000;
+
+ // Fill DASH header
+ p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_HEADER,
+ // availabilityStartTime
+ available_time,
+ // publishTime
+ publish_time,
+ // minimumUpdatePeriod
+ update_period, update_period_msec,
+ // minBufferTime
+ buffer_time, buffer_time_msec,
+ // timeShiftBufferDepth
+ buffer_depth,
+ // suggestedPresentationDelay
+ presentation_delay, presentation_delay_msec
+ );
+
+ p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_PERIOD);
+
+ n = ngx_write_fd(fd, buffer, p - buffer);
+
+ sep = (dacf->nested ? "/" : "-");
+ var = dacf->variant->elts;
+
+ if (ctx->has_video) {
+ frame_rate_num = (ngx_uint_t) (codec_ctx->frame_rate * 1000.);
+
+ if (frame_rate_num % 1000 == 0) {
+ *ngx_sprintf(frame_rate, "%ui", frame_rate_num / 1000) = 0;
+ } else {
+ frame_rate_denom = 1000;
+ switch (frame_rate_num) {
+ case 23976:
+ frame_rate_num = 24000;
+ frame_rate_denom = 1001;
+ break;
+ case 29970:
+ frame_rate_num = 30000;
+ frame_rate_denom = 1001;
+ break;
+ case 59940:
+ frame_rate_num = 60000;
+ frame_rate_denom = 1001;
+ break;
+ }
+
+ *ngx_sprintf(frame_rate, "%ui/%ui", frame_rate_num, frame_rate_denom) = 0;
+ }
+
+ gcd = ngx_rtmp_dash_gcd(codec_ctx->width, codec_ctx->height);
+ par_x = codec_ctx->width / gcd;
+ par_y = codec_ctx->height / gcd;
+
+ p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO,
+ codec_ctx->width,
+ codec_ctx->height,
+ frame_rate,
+ par_x, par_y);
+
+ switch (dacf->ad_markers) {
+ case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT:
+ case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35:
+ p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT);
+ }
+
+ if (dacf->cenc) {
+ p = ngx_rtmp_dash_write_content_protection(s, &ctx->drm_info, p, last);
+ }
+
+ n = ngx_write_fd(fd, buffer, p - buffer);
+
+ for (j = 0; j < dacf->variant->nelts; j++, var++) {
+
+ /* read segments file */
+ if (dacf->nested) {
+ *ngx_sprintf(seg_path, "%V/%V%V/index.seg",
+ &dacf->path, &ctx->varname, &var->suffix) = 0;
+ } else {
+ *ngx_sprintf(seg_path, "%V/%V%V.seg",
+ &dacf->path, &ctx->varname, &var->suffix) = 0;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ "dash: read segments file for variant '%s'", seg_path);
+
+ fds = ngx_open_file(seg_path, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
+
+ if (fds == NGX_INVALID_FILE) {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
+ "dash: open failed: segments '%s'", seg_path);
+ continue;
+ }
+
+ p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VARIANT_VIDEO,
+ &ctx->varname, &var->suffix,
+ codec_ctx->avc_profile,
+ codec_ctx->avc_compat,
+ codec_ctx->avc_level);
+
+ arg = var->args.elts;
+ for (k = 0; k < var->args.nelts && k < 3 ; k++, arg++) {
+ p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_VARIANT_ARG, arg);
+ }
+
+ p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_VARIANT_ARG_FOOTER);
+
+ p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_SEGMENTTPL_VARIANT_VIDEO,
+ &ctx->varname, &var->suffix, sep,
+ &ctx->varname, &var->suffix, sep);
+
+ n = ngx_write_fd(fd, buffer, p - buffer);
+
+ while ((n = ngx_read_fd(fds, buffer, sizeof(buffer)))) {
+ n = ngx_write_fd(fd, buffer, n);
+ }
+
+ ngx_close_file(fds);
+
+ p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO_FOOTER);
+ n = ngx_write_fd(fd, buffer, p - buffer);
+
+ }
+
+ p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO_FOOTER);
+ n = ngx_write_fd(fd, buffer, p - buffer);
+ }
+
+ if (ctx->has_audio) {
+ p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO);
+
+ if (dacf->cenc) {
+ p = ngx_rtmp_dash_write_content_protection(s, &ctx->drm_info, p, last);
+ }
+
+ p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO,
+ &ctx->name,
+ codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC ?
+ (codec_ctx->aac_sbr ? "40.5" : "40.2") : "6b",
+ codec_ctx->sample_rate,
+ (ngx_uint_t) (codec_ctx->audio_data_rate * 1000),
+ &ctx->name, sep,
+ &ctx->name, sep);
+
+ p = ngx_rtmp_dash_write_segment_timeline(s, ctx, dacf, p, last);
+
+ p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO_FOOTER);
+
+ p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO_FOOTER);
+
+ n = ngx_write_fd(fd, buffer, p - buffer);
+ }
+
+ p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_PERIOD_FOOTER);
+ n = ngx_write_fd(fd, buffer, p - buffer);
+
+ /* UTCTiming value */
+ switch (dacf->clock_compensation) {
+ case NGX_RTMP_DASH_CLOCK_COMPENSATION_NTP:
+ p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_CLOCK,
+ "ntp",
+ &dacf->clock_helper_uri
+ );
+ n = ngx_write_fd(fd, buffer, p - buffer);
+ break;
+ case NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_HEAD:
+ p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_CLOCK,
+ "http-head",
+ &dacf->clock_helper_uri
+ );
+ n = ngx_write_fd(fd, buffer, p - buffer);
+ break;
+ case NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_ISO:
+ p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_CLOCK,
+ "http-iso",
+ &dacf->clock_helper_uri
+ );
+ n = ngx_write_fd(fd, buffer, p - buffer);
+ break;
+ }
+
+ p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_FOOTER);
+ n = ngx_write_fd(fd, buffer, p - buffer);
+
+ if (n < 0) {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
+ "dash: write failed: '%V'", &ctx->var_playlist_bak);
+ ngx_close_file(fd);
+ return NGX_ERROR;
+ }
+
+ ngx_close_file(fd);
+
+ if (ngx_rtmp_dash_rename_file(ctx->var_playlist_bak.data, ctx->var_playlist.data)
+ == NGX_FILE_ERROR)
+ {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
+ "dash: rename failed: '%V'->'%V'",
+ &ctx->var_playlist_bak, &ctx->var_playlist);
+ return NGX_ERROR;
}
- return m;
+
+ ngx_memzero(&v, sizeof(v));
+ ngx_str_set(&(v.module), "dash");
+ v.playlist.data = ctx->playlist.data;
+ v.playlist.len = ctx->playlist.len;
+ return next_playlist(s, &v);
}
@@ -269,13 +839,13 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
char *sep;
u_char *p, *last;
ssize_t n;
- ngx_fd_t fd;
+ ngx_fd_t fd, fds;
struct tm tm;
ngx_str_t noname, *name;
ngx_uint_t i, frame_rate_num, frame_rate_denom;
ngx_uint_t depth_msec, depth_sec;
ngx_uint_t update_period, update_period_msec;
- ngx_uint_t buffer_time, buffer_time_msec;
+ ngx_uint_t start_time, buffer_time, buffer_time_msec;
ngx_uint_t presentation_delay, presentation_delay_msec;
ngx_uint_t gcd, par_x, par_y;
ngx_rtmp_dash_ctx_t *ctx;
@@ -286,7 +856,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
ngx_rtmp_playlist_t v;
static u_char buffer[NGX_RTMP_DASH_BUFSIZE];
- static u_char avaliable_time[NGX_RTMP_DASH_GMT_LENGTH];
+ static u_char available_time[NGX_RTMP_DASH_GMT_LENGTH];
static u_char publish_time[NGX_RTMP_DASH_GMT_LENGTH];
static u_char buffer_depth[sizeof("P00Y00M00DT00H00M00.000S")];
static u_char frame_rate[(NGX_INT_T_LEN * 2) + 2];
@@ -300,7 +870,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
}
if (ctx->id == 0) {
- ngx_rtmp_dash_write_init_segments(s);
+ ngx_rtmp_dash_write_init_segments(s, &ctx->drm_info);
}
fd = ngx_open_file(ctx->playlist_bak.data, NGX_FILE_WRONLY,
@@ -311,121 +881,47 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
"dash: open failed: '%V'", &ctx->playlist_bak);
return NGX_ERROR;
}
+
+ /* write segments file */
+ fds = ngx_open_file(ctx->segments_bak.data, NGX_FILE_WRONLY,
+ NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS);
+
+ if (fds == NGX_INVALID_FILE) {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
+ "dash: open failed: '%V'", &ctx->segments_bak);
+ return NGX_ERROR;
+ }
+
+ /* availabity and publish time should be relative to peer epoch */
+ start_time = ctx->start_time.sec - (s->peer_epoch/1000);
+ ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ "Fixing start_time=%uD %uD epoch=%uD new_start_time=%uD",
+ (uint32_t)ctx->start_time.sec, (uint32_t)ctx->start_time.msec,
+ (uint32_t)s->peer_epoch,
+ (uint32_t)start_time);
+ /**
+ * Availability time must be equal stream start time
+ * Cos segments time counting from it
+ */
+ ngx_libc_gmtime(start_time, &tm);
-#define NGX_RTMP_DASH_MANIFEST_HEADER \
- "\n" \
- "\n"
-
-#define NGX_RTMP_DASH_MANIFEST_PERIOD \
- " \n"
-
-
-#define NGX_RTMP_DASH_MANIFEST_VIDEO \
- " \n" \
- " \n" \
- " \n" \
- " \n"
-
-
-#define NGX_RTMP_DASH_MANIFEST_VIDEO_FOOTER \
- " \n" \
- " \n" \
- " \n" \
- " \n"
-
-
-#define NGX_RTMP_DASH_MANIFEST_TIME \
- " \n"
-
-
-#define NGX_RTMP_DASH_MANIFEST_AUDIO \
- " \n" \
- " \n" \
- " \n" \
- " \n" \
- " \n"
-
-
-#define NGX_RTMP_DASH_MANIFEST_AUDIO_FOOTER \
- " \n" \
- " \n" \
- " \n" \
- " \n"
-
-
-#define NGX_RTMP_DASH_PERIOD_FOOTER \
- " \n"
-
-
-#define NGX_RTMP_DASH_MANIFEST_CLOCK \
- " \n"
-
-
-#define NGX_RTMP_DASH_MANIFEST_FOOTER \
- "\n"
-
-
-/**
- * Availability time must be equal stream start time
- * Cos segments time counting from it
- */
- ngx_libc_gmtime(ctx->start_time.sec, &tm);
- *ngx_sprintf(avaliable_time, "%4d-%02d-%02dT%02d:%02d:%02dZ",
+ *ngx_sprintf(available_time, "%4d-%02d-%02dT%02d:%02d:%02dZ",
tm.tm_year + 1900, tm.tm_mon + 1,
tm.tm_mday, tm.tm_hour,
tm.tm_min, tm.tm_sec
) = 0;
+ /*
+ *ngx_sprintf(available_time, "%4d-%02d-%02dT%02d:%02d:%02dZ",
+ 1970, 1,
+ 1, 0,
+ 0, 0
+ ) = 0;
+ */
+
/* Stream publish time */
- *ngx_sprintf(publish_time, "%s", avaliable_time) = 0;
+ *ngx_sprintf(publish_time, "%s", available_time) = 0;
depth_sec = (ngx_uint_t) (
ngx_rtmp_dash_get_frag(s, ctx->nfrags - 1)->timestamp +
@@ -481,7 +977,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
// Fill DASH header
p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_HEADER,
// availabilityStartTime
- avaliable_time,
+ available_time,
// publishTime
publish_time,
// minimumUpdatePeriod
@@ -532,11 +1028,23 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
par_x = codec_ctx->width / gcd;
par_y = codec_ctx->height / gcd;
- p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_VIDEO,
+ p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO,
codec_ctx->width,
codec_ctx->height,
frame_rate,
- par_x, par_y,
+ par_x, par_y);
+
+ switch (dacf->ad_markers) {
+ case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT:
+ case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35:
+ p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT);
+ }
+
+ if (dacf->cenc) {
+ p = ngx_rtmp_dash_write_content_protection(s, &ctx->drm_info, p, last);
+ }
+
+ p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO,
&ctx->name,
codec_ctx->avc_profile,
codec_ctx->avc_compat,
@@ -548,19 +1056,28 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
name, sep,
name, sep);
- for (i = 0; i < ctx->nfrags; i++) {
- f = ngx_rtmp_dash_get_frag(s, i);
- p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME,
- f->timestamp, f->duration);
- }
+ n = ngx_write_fd(fd, buffer, p - buffer);
+
+ p = buffer;
+ p = ngx_rtmp_dash_write_segment_timeline(s, ctx, dacf, p, last);
+
+ ngx_write_fd(fds, buffer, p - buffer);
- p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_VIDEO_FOOTER);
+ p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO_FOOTER);
+
+ p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO_FOOTER);
n = ngx_write_fd(fd, buffer, p - buffer);
}
if (ctx->has_audio) {
- p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_AUDIO,
+ p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO);
+
+ if (dacf->cenc) {
+ p = ngx_rtmp_dash_write_content_protection(s, &ctx->drm_info, p, last);
+ }
+
+ p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO,
&ctx->name,
codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC ?
(codec_ctx->aac_sbr ? "40.5" : "40.2") : "6b",
@@ -569,13 +1086,11 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
name, sep,
name, sep);
- for (i = 0; i < ctx->nfrags; i++) {
- f = ngx_rtmp_dash_get_frag(s, i);
- p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME,
- f->timestamp, f->duration);
- }
+ p = ngx_rtmp_dash_write_segment_timeline(s, ctx, dacf, p, last);
+
+ p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO_FOOTER);
- p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_AUDIO_FOOTER);
+ p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO_FOOTER);
n = ngx_write_fd(fd, buffer, p - buffer);
}
@@ -619,6 +1134,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
}
ngx_close_file(fd);
+ ngx_close_file(fds);
if (ngx_rtmp_dash_rename_file(ctx->playlist_bak.data, ctx->playlist.data)
== NGX_FILE_ERROR)
@@ -629,6 +1145,20 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
return NGX_ERROR;
}
+ if (ngx_rtmp_dash_rename_file(ctx->segments_bak.data, ctx->segments.data)
+ == NGX_FILE_ERROR)
+ {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
+ "dash: rename failed: '%V'->'%V'",
+ &ctx->segments_bak, &ctx->segments);
+ return NGX_ERROR;
+ }
+
+ /* try to write the variant file only once, check the max flag */
+ if (ctx->var && ctx->var->args.nelts > 3) {
+ return ngx_rtmp_dash_write_variant_playlist(s);
+ }
+
ngx_memzero(&v, sizeof(v));
ngx_str_set(&(v.module), "dash");
v.playlist.data = ctx->playlist.data;
@@ -638,20 +1168,22 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
static ngx_int_t
-ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s)
+ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s, ngx_rtmp_cenc_drm_info_t *drmi)
{
- ngx_fd_t fd;
- ngx_int_t rc;
- ngx_buf_t b;
- ngx_rtmp_dash_ctx_t *ctx;
- ngx_rtmp_codec_ctx_t *codec_ctx;
+ ngx_fd_t fd;
+ ngx_int_t rc;
+ ngx_buf_t b;
+ ngx_rtmp_dash_ctx_t *ctx;
+ ngx_rtmp_codec_ctx_t *codec_ctx;
+ ngx_rtmp_dash_app_conf_t *dacf;
static u_char buffer[NGX_RTMP_DASH_BUFSIZE];
+ dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module);
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module);
codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
- if (ctx == NULL || codec_ctx == NULL) {
+ if (dacf == NULL || ctx == NULL || codec_ctx == NULL) {
return NGX_ERROR;
}
@@ -673,7 +1205,11 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s)
b.pos = b.last = b.start;
ngx_rtmp_mp4_write_ftyp(&b);
- ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_VIDEO_TRACK);
+ if (dacf->cenc) {
+ ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_EVIDEO_TRACK, drmi);
+ } else {
+ ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_VIDEO_TRACK, NULL);
+ }
rc = ngx_write_fd(fd, b.start, (size_t) (b.last - b.start));
if (rc == NGX_ERROR) {
@@ -699,7 +1235,11 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s)
b.pos = b.last = b.start;
ngx_rtmp_mp4_write_ftyp(&b);
- ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_AUDIO_TRACK);
+ if (dacf->cenc) {
+ ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_EAUDIO_TRACK, drmi);
+ } else {
+ ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_AUDIO_TRACK, NULL);
+ }
rc = ngx_write_fd(fd, b.start, (size_t) (b.last - b.start));
if (rc == NGX_ERROR) {
@@ -722,6 +1262,7 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t)
ngx_fd_t fd;
ngx_buf_t b;
ngx_rtmp_dash_ctx_t *ctx;
+ ngx_rtmp_dash_app_conf_t *dacf;
ngx_rtmp_dash_frag_t *f;
static u_char buffer[NGX_RTMP_DASH_BUFSIZE];
@@ -735,18 +1276,69 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t)
t->id, t->type, t->earliest_pres_time);
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module);
+ dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module);
b.start = buffer;
b.end = buffer + sizeof(buffer);
b.pos = b.last = b.start;
+ if (ctx->start_cuepoint) {
+
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "dash : onCuepoint write start emsg : epts='%uD', lpts='%uD', cpts='%uD', "\
+ "ecpts='%uD', duration='%uD', prid='%uD'",
+ t->earliest_pres_time, t->latest_pres_time, ctx->cuepoint_starttime,
+ ctx->cuepoint_endtime, ctx->cuepoint_duration, ctx->cuepoint_id);
+
+ if (dacf->ad_markers_timehack) {
+ /* dashjs bug : use delta as an absolute timestamp */
+ ngx_rtmp_mp4_write_emsg(&b, 0,
+ ctx->cuepoint_starttime,
+ ctx->cuepoint_duration,
+ ctx->cuepoint_id);
+ } else {
+ ngx_rtmp_mp4_write_emsg(&b, t->earliest_pres_time,
+ ctx->cuepoint_starttime,
+ ctx->cuepoint_duration,
+ ctx->cuepoint_id);
+ }
+
+ pos = b.last;
+ b.last = pos;
+ ctx->start_cuepoint = 0;
+ ctx->cuepoint_duration = 0;
+ ctx->end_cuepoint = 1;
+
+ } else if (ctx->end_cuepoint && ctx->cuepoint_endtime >= t->earliest_pres_time
+ && ctx->cuepoint_endtime <= t->latest_pres_time) {
+
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "dash : onCuepoint write end emsg : epts='%uD', lpts='%uD', cpts='%uD', "\
+ "ecpts='%uD', duration='%uD', prid='%uD'",
+ t->earliest_pres_time, t->latest_pres_time, ctx->cuepoint_starttime,
+ ctx->cuepoint_endtime, ctx->cuepoint_duration, ctx->cuepoint_id);
+
+ /* end marker have duration set to zero and prid set to zero */
+ ngx_rtmp_mp4_write_emsg(&b, 0,
+ ctx->cuepoint_endtime,
+ 0,
+ 0);
+
+ pos = b.last;
+ b.last = pos;
+ ctx->end_cuepoint = 0;
+ } else if (ctx->end_cuepoint && ctx->cuepoint_endtime < t->earliest_pres_time ) {
+ /* fallback */
+ ctx->end_cuepoint = 0;
+ }
+
ngx_rtmp_mp4_write_styp(&b);
pos = b.last;
b.last += 44; /* leave room for sidx */
- ngx_rtmp_mp4_write_moof(&b, t->earliest_pres_time, t->sample_count,
- t->samples, t->sample_mask, t->id);
+ ngx_rtmp_mp4_write_moof(&b, t->earliest_pres_time, t->type, t->sample_count,
+ t->samples, t->sample_mask, t->id, t->is_protected);
pos1 = b.last;
b.last = pos;
@@ -762,6 +1354,9 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t)
*ngx_sprintf(ctx->stream.data + ctx->stream.len, "%uD.m4%c",
f->timestamp, t->type) = 0;
+ //*ngx_sprintf(ctx->stream.data + ctx->stream.len, "%uL.m4%c",
+ // f->u_timestamp, t->type) = 0;
+
fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR,
NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS);
@@ -850,7 +1445,8 @@ static ngx_int_t
ngx_rtmp_dash_open_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t,
ngx_uint_t id, char type)
{
- ngx_rtmp_dash_ctx_t *ctx;
+ ngx_rtmp_dash_ctx_t *ctx;
+ ngx_rtmp_dash_app_conf_t *dacf;
if (t->opened) {
return NGX_OK;
@@ -860,6 +1456,7 @@ ngx_rtmp_dash_open_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t,
"dash: open fragment id=%ui, type='%c'", id, type);
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module);
+ dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module);
*ngx_sprintf(ctx->stream.data + ctx->stream.len, "raw.m4%c", type) = 0;
@@ -879,6 +1476,17 @@ ngx_rtmp_dash_open_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t,
t->latest_pres_time = 0;
t->mdat_size = 0;
t->opened = 1;
+
+ if (dacf->cenc) {
+
+ if (ngx_rtmp_cenc_read_hex(dacf->cenc_key, t->key) == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "dash: error cenc key is invalid");
+ return NGX_ERROR;
+ }
+
+ t->is_protected = 1;
+ }
if (type == 'v') {
t->sample_mask = NGX_RTMP_MP4_SAMPLE_SIZE|
@@ -1022,11 +1630,13 @@ ngx_rtmp_dash_ensure_directory(ngx_rtmp_session_t *s)
static ngx_int_t
ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
{
- u_char *p;
+ u_char *p, *pp;
size_t len;
ngx_rtmp_dash_ctx_t *ctx;
ngx_rtmp_dash_frag_t *f;
ngx_rtmp_dash_app_conf_t *dacf;
+ ngx_rtmp_dash_variant_t *var;
+ ngx_uint_t n;
dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module);
if (dacf == NULL || !dacf->dash || dacf->path.len == 0) {
@@ -1113,6 +1723,53 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
ngx_memcpy(ctx->stream.data, ctx->playlist.data, ctx->stream.len - 1);
ctx->stream.data[ctx->stream.len - 1] = (dacf->nested ? '/' : '-');
+ if (dacf->variant) {
+ var = dacf->variant->elts;
+ for (n = 0; n < dacf->variant->nelts; n++, var++) {
+ if (ctx->name.len > var->suffix.len &&
+ ngx_memcmp(var->suffix.data,
+ ctx->name.data + ctx->name.len - var->suffix.len,
+ var->suffix.len)
+ == 0)
+ {
+ len = (size_t) (ctx->name.len - var->suffix.len);
+
+ ctx->varname.len = len;
+ ctx->varname.data = ngx_palloc(s->connection->pool,
+ ctx->varname.len + 1);
+ pp = ngx_cpymem(ctx->varname.data,
+ ctx->name.data, len);
+
+ *pp = 0;
+
+ ctx->var = var;
+
+ len = (size_t) (p - ctx->playlist.data);
+
+ ctx->var_playlist.len = len - var->suffix.len + sizeof(".mpd")
+ -1;
+ ctx->var_playlist.data = ngx_palloc(s->connection->pool,
+ ctx->var_playlist.len + 1);
+ pp = ngx_cpymem(ctx->var_playlist.data,
+ ctx->playlist.data, len - var->suffix.len);
+ pp = ngx_cpymem(pp, ".mpd", sizeof(".mpd") - 1);
+ *pp = 0;
+
+ ctx->var_playlist_bak.len = ctx->var_playlist.len +
+ sizeof(".bak") - 1;
+ ctx->var_playlist_bak.data = ngx_palloc(s->connection->pool,
+ ctx->var_playlist_bak.len + 1);
+ pp = ngx_cpymem(ctx->var_playlist_bak.data,
+ ctx->var_playlist.data,
+ ctx->var_playlist.len);
+ pp = ngx_cpymem(pp, ".bak", sizeof(".bak") - 1);
+ *pp = 0;
+
+ break;
+ }
+ }
+ }
+
if (dacf->nested) {
p = ngx_cpymem(p, "/index.mpd", sizeof("/index.mpd") - 1);
} else {
@@ -1135,15 +1792,57 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
*p = 0;
- ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
- "dash: playlist='%V' playlist_bak='%V' stream_pattern='%V'",
- &ctx->playlist, &ctx->playlist_bak, &ctx->stream);
+ /* segments path */
+
+ ctx->segments.data = ngx_palloc(s->connection->pool,
+ ctx->playlist.len - 4 + sizeof(".seg"));
+ p = ngx_cpymem(ctx->segments.data, ctx->playlist.data,
+ ctx->playlist.len - 4);
+ p = ngx_cpymem(p, ".seg", sizeof(".seg") - 1);
+
+ ctx->segments.len = p - ctx->segments.data;
+
+ *p = 0;
+
+ /* segments bak (new segments) path */
+
+ ctx->segments_bak.data = ngx_palloc(s->connection->pool,
+ ctx->playlist.len - 4 + sizeof(".sbk"));
+ p = ngx_cpymem(ctx->segments_bak.data, ctx->playlist.data,
+ ctx->playlist.len - 4);
+ p = ngx_cpymem(p, ".sbk", sizeof(".sbk") - 1);
+
+ ctx->segments_bak.len = p - ctx->segments_bak.data;
+
+ *p = 0;
+
+ ngx_log_debug5(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ "dash: playlist='%V' playlist_bak='%V' segments='%V' segments_bak='%V' stream_pattern='%V'",
+ &ctx->playlist, &ctx->playlist_bak, &ctx->segments, &ctx->segments_bak, &ctx->stream);
ctx->start_time = *ngx_cached_time;
if (ngx_rtmp_dash_ensure_directory(s) != NGX_OK) {
return NGX_ERROR;
}
+
+ /* drm info */
+ if (dacf->cenc) {
+ if (ngx_rtmp_cenc_read_hex(dacf->cenc_kid, ctx->drm_info.kid) == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "dash: error cenc kid is invalid");
+ return NGX_ERROR;
+ }
+ if (dacf->wdv) {
+ ctx->drm_info.wdv = 1;
+ ctx->drm_info.wdv_data = dacf->wdv_data;
+ }
+ if (dacf->mspr) {
+ ctx->drm_info.mspr = 1;
+ ctx->drm_info.mspr_data = dacf->mspr_data;
+ ctx->drm_info.mspr_kid = dacf->mspr_kid;
+ ctx->drm_info.mspr_pro = dacf->mspr_pro;
+ }
+ }
next:
return next_publish(s, v);
@@ -1173,7 +1872,6 @@ ngx_rtmp_dash_close_stream(ngx_rtmp_session_t *s, ngx_rtmp_close_stream_t *v)
return next_close_stream(s, v);
}
-
static void
ngx_rtmp_dash_update_fragments(ngx_rtmp_session_t *s, ngx_int_t boundary,
uint32_t timestamp)
@@ -1202,7 +1900,6 @@ ngx_rtmp_dash_update_fragments(ngx_rtmp_session_t *s, ngx_int_t boundary,
} else {
/* sometimes clients generate slightly unordered frames */
-
hit = (-d > 1000);
}
@@ -1249,7 +1946,9 @@ ngx_rtmp_dash_update_fragments(ngx_rtmp_session_t *s, ngx_int_t boundary,
ngx_rtmp_dash_open_fragments(s);
f = ngx_rtmp_dash_get_frag(s, ctx->nfrags);
+
f->timestamp = timestamp;
+ f->u_timestamp = ngx_time() * 1000;
}
}
@@ -1258,11 +1957,11 @@ static ngx_int_t
ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in,
ngx_rtmp_dash_track_t *t, ngx_int_t key, uint32_t timestamp, uint32_t delay)
{
- u_char *p;
- size_t size, bsize;
- ngx_rtmp_mp4_sample_t *smpl;
+ u_char *p;
+ size_t size, bsize, csize;
+ ngx_rtmp_mp4_sample_t *smpl;
- static u_char buffer[NGX_RTMP_DASH_BUFSIZE];
+ static u_char buffer[NGX_RTMP_DASH_BUFSIZE];
p = buffer;
size = 0;
@@ -1282,18 +1981,35 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in,
if (t->sample_count == 0) {
t->earliest_pres_time = timestamp;
+ if (t->is_protected) {
+ ngx_rtmp_cenc_rand_iv(t->iv);
+ }
}
t->latest_pres_time = timestamp;
if (t->sample_count < NGX_RTMP_DASH_MAX_SAMPLES) {
+ if (t->is_protected) {
+ if (t->type == 'v') {
+ ngx_rtmp_cenc_encrypt_sub_sample(s, t->key, t->iv, buffer, size, &csize);
+ ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ "dash: cenc crypt video sample: count=%ui, key=%ui, size=%ui, csize=%ui",
+ t->sample_count, key, size, csize);
+ } else {
+ ngx_rtmp_cenc_encrypt_full_sample(s, t->key, t->iv, buffer, size);
+ ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ "dash: cenc crypt audio sample: count=%ui, key=%ui, size=%ui",
+ t->sample_count, key, size);
+ }
+ }
+
if (ngx_write_fd(t->fd, buffer, size) == NGX_ERROR) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
"dash: " ngx_write_fd_n " failed");
return NGX_ERROR;
- }
-
+ }
+
smpl = &t->samples[t->sample_count];
smpl->delay = delay;
@@ -1302,6 +2018,15 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in,
smpl->timestamp = timestamp;
smpl->key = (key ? 1 : 0);
+ if (t->is_protected) {
+ smpl->is_protected = 1;
+ ngx_memcpy(smpl->iv, t->iv, NGX_RTMP_CENC_IV_SIZE);
+ ngx_rtmp_cenc_increment_iv(t->iv);
+ if (t->type == 'v') {
+ smpl->clear_size = (uint32_t) csize;
+ }
+ }
+
if (t->sample_count > 0) {
smpl = &t->samples[t->sample_count - 1];
smpl->duration = timestamp - smpl->timestamp;
@@ -1394,6 +2119,7 @@ ngx_rtmp_dash_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
return NGX_ERROR;
}
+ /* check what header it is */
ftype = (in->buf->pos[0] & 0xf0) >> 4;
/* skip AVC config */
@@ -1621,6 +2347,7 @@ ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen)
}
}
+
#if (nginx_version >= 1011005)
static ngx_msec_t
#else
@@ -1641,12 +2368,365 @@ ngx_rtmp_dash_cleanup(void *data)
#endif
}
+
+static char *
+ngx_rtmp_dash_variant(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_rtmp_dash_app_conf_t *dacf = conf;
+
+ ngx_str_t *value, *arg;
+ ngx_uint_t n;
+ ngx_rtmp_dash_variant_t *var;
+
+ value = cf->args->elts;
+
+ if (dacf->variant == NULL) {
+ dacf->variant = ngx_array_create(cf->pool, 1,
+ sizeof(ngx_rtmp_dash_variant_t));
+ if (dacf->variant == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ var = ngx_array_push(dacf->variant);
+ if (var == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(var, sizeof(ngx_rtmp_dash_variant_t));
+
+ var->suffix = value[1];
+
+ if (cf->args->nelts == 2) {
+ return NGX_CONF_OK;
+ }
+
+ if (ngx_array_init(&var->args, cf->pool, cf->args->nelts - 2,
+ sizeof(ngx_str_t))
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ arg = ngx_array_push_n(&var->args, cf->args->nelts - 2);
+ if (arg == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ for (n = 2; n < cf->args->nelts; n++) {
+ *arg++ = value[n];
+ }
+
+ return NGX_CONF_OK;
+}
+
+
static ngx_int_t
ngx_rtmp_dash_playlist(ngx_rtmp_session_t *s, ngx_rtmp_playlist_t *v)
{
return next_playlist(s, v);
}
+
+static ngx_int_t
+ngx_rtmp_dash_on_cuepoint(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
+ ngx_chain_t *in)
+{
+ ngx_int_t res;
+ ngx_rtmp_dash_ctx_t *ctx;
+
+ ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module);
+
+ static struct {
+ double time;
+ double duration;
+ u_char name[128];
+ u_char type[128];
+ u_char ptype[128];
+ } v;
+
+ static ngx_rtmp_amf_elt_t in_pr_elts[] = {
+
+ { NGX_RTMP_AMF_STRING,
+ ngx_string("type"),
+ v.ptype, sizeof(v.ptype) },
+
+ { NGX_RTMP_AMF_NUMBER,
+ ngx_string("duration"),
+ &v.duration, sizeof(v.duration) },
+
+ };
+
+ static ngx_rtmp_amf_elt_t in_dt_elts[] = {
+
+ { NGX_RTMP_AMF_NUMBER,
+ ngx_string("time"),
+ &v.time, sizeof(v.time) },
+
+ { NGX_RTMP_AMF_STRING,
+ ngx_string("name"),
+ v.name, sizeof(v.name) },
+
+ { NGX_RTMP_AMF_STRING,
+ ngx_string("type"),
+ v.type, sizeof(v.type) },
+
+ { NGX_RTMP_AMF_OBJECT,
+ ngx_string("parameters"),
+ in_pr_elts, sizeof(in_pr_elts) },
+
+ };
+
+ static ngx_rtmp_amf_elt_t in_elts[] = {
+
+ { NGX_RTMP_AMF_OBJECT,
+ ngx_null_string,
+ in_dt_elts, sizeof(in_dt_elts) },
+
+ };
+
+ ngx_memzero(&v, sizeof(v));
+ res = ngx_rtmp_receive_amf(s, in, in_elts,
+ sizeof(in_elts) / sizeof(in_elts[0]));
+
+ if (res == NGX_OK && v.duration > 0) {
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "dash : onCuepoint : ts='%ui', time='%f', name='%s' type='%s' ptype='%s' duration='%f'",
+ h->timestamp, v.time, v.name, v.type, v.ptype, v.duration);
+ ctx->start_cuepoint = 1;
+ ctx->cuepoint_starttime = h->timestamp;
+ ctx->cuepoint_endtime = h->timestamp+(v.duration*1000);
+ ctx->cuepoint_duration = v.duration;
+ ctx->cuepoint_id = 0;
+ } else {
+ ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ "dash : onCuepoint : amf not understood");
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_rtmp_dash_on_cuepoint_scte35(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
+ ngx_chain_t *in)
+{
+ ngx_int_t res;
+ ngx_rtmp_dash_ctx_t *ctx;
+
+ ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module);
+
+ static struct {
+ unsigned ooni;
+ unsigned splice_event_ci;
+ unsigned splice_imd;
+ double avail_num;
+ double avail_expected;
+ double duration;
+ double prtime;
+ double prgid;
+ double sctype;
+ double sevtid;
+ u_char type[128];
+ u_char mtype[128];
+ } v;
+
+ static ngx_rtmp_amf_elt_t in_pr_elts[] = {
+
+ { NGX_RTMP_AMF_STRING,
+ ngx_string("messageType"),
+ v.mtype, sizeof(v.mtype) },
+
+ { NGX_RTMP_AMF_NUMBER,
+ ngx_string("splice_command_type"),
+ &v.sctype, sizeof(v.sctype) },
+
+ { NGX_RTMP_AMF_NUMBER,
+ ngx_string("splice_event_id"),
+ &v.sevtid, sizeof(v.sevtid) },
+
+ { NGX_RTMP_AMF_BOOLEAN,
+ ngx_string("splice_event_cancel_indicator"),
+ &v.splice_event_ci, sizeof(v.splice_event_ci) },
+
+ { NGX_RTMP_AMF_BOOLEAN,
+ ngx_string("out_of_network_indicator"),
+ &v.ooni, sizeof(v.ooni) },
+
+ { NGX_RTMP_AMF_BOOLEAN,
+ ngx_string("splice_immediate"),
+ &v.splice_imd, sizeof(v.splice_imd) },
+
+ { NGX_RTMP_AMF_NUMBER,
+ ngx_string("pre_roll_time"),
+ &v.prtime, sizeof(v.prtime) },
+
+ { NGX_RTMP_AMF_NUMBER,
+ ngx_string("break_duration"),
+ &v.duration, sizeof(v.duration) },
+
+ { NGX_RTMP_AMF_NUMBER,
+ ngx_string("unique_program_id"),
+ &v.prgid, sizeof(v.prgid) },
+
+ { NGX_RTMP_AMF_NUMBER,
+ ngx_string("avail_num"),
+ &v.avail_num, sizeof(v.avail_num) },
+
+ { NGX_RTMP_AMF_NUMBER,
+ ngx_string("avail_expected"),
+ &v.avail_expected, sizeof(v.avail_expected) },
+
+ };
+
+ static ngx_rtmp_amf_elt_t in_dt_elts[] = {
+
+ { NGX_RTMP_AMF_STRING,
+ ngx_string("type"),
+ v.type, sizeof(v.type) },
+
+ { NGX_RTMP_AMF_OBJECT,
+ ngx_string("parameters"),
+ in_pr_elts, sizeof(in_pr_elts) },
+
+ };
+
+ static ngx_rtmp_amf_elt_t in_elts[] = {
+
+ { NGX_RTMP_AMF_OBJECT,
+ ngx_null_string,
+ in_dt_elts, sizeof(in_dt_elts) },
+
+ };
+
+ ngx_memzero(&v, sizeof(v));
+ res = ngx_rtmp_receive_amf(s, in, in_elts,
+ sizeof(in_elts) / sizeof(in_elts[0]));
+
+ if (res == NGX_OK && v.duration > 0) {
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "dash : onCuepoint_scte35 : ts='%ui', type='%s', mtype='%s', sctype='%f', "\
+ "scid='%f', prgid='%f', duration='%f', avail_num='%f', avail_expected='%f'",
+ h->timestamp, v.type, v.mtype, v.sctype,
+ v.sevtid, v.prgid, v.duration, v.avail_num, v.avail_expected);
+ ctx->start_cuepoint = 1;
+ ctx->cuepoint_starttime = h->timestamp;
+ ctx->cuepoint_endtime = h->timestamp + v.duration;
+ ctx->cuepoint_duration = v.duration;
+ ctx->cuepoint_id = v.prgid;
+ } else {
+ ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ "dash : onCuepoint_scte35 : amf not understood");
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_rtmp_dash_on_cuepoint_cont_scte35(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
+ ngx_chain_t *in)
+{
+ ngx_int_t res;
+
+ static struct {
+ double segupid;
+ double segduration;
+ double segrduration;
+ u_char type[128];
+ u_char mtype[128];
+ } v;
+
+ static ngx_rtmp_amf_elt_t in_pr_elts[] = {
+
+ { NGX_RTMP_AMF_STRING,
+ ngx_string("messageType"),
+ v.mtype, sizeof(v.mtype) },
+
+ { NGX_RTMP_AMF_NUMBER,
+ ngx_string("segmentation_upid"),
+ &v.segupid, sizeof(v.segupid) },
+
+ { NGX_RTMP_AMF_NUMBER,
+ ngx_string("segmentation_duration"),
+ &v.segduration, sizeof(v.segduration) },
+
+ { NGX_RTMP_AMF_NUMBER,
+ ngx_string("segmentation_remaining_duration"),
+ &v.segrduration, sizeof(v.segrduration) },
+
+ };
+
+ static ngx_rtmp_amf_elt_t in_dt_elts[] = {
+
+ { NGX_RTMP_AMF_STRING,
+ ngx_string("type"),
+ v.type, sizeof(v.type) },
+
+ { NGX_RTMP_AMF_OBJECT,
+ ngx_string("parameters"),
+ in_pr_elts, sizeof(in_pr_elts) },
+
+ };
+
+ static ngx_rtmp_amf_elt_t in_elts[] = {
+
+ { NGX_RTMP_AMF_OBJECT,
+ ngx_null_string,
+ in_dt_elts, sizeof(in_dt_elts) },
+
+ };
+
+ ngx_memzero(&v, sizeof(v));
+ res = ngx_rtmp_receive_amf(s, in, in_elts,
+ sizeof(in_elts) / sizeof(in_elts[0]));
+
+ if (res == NGX_OK && v.segrduration > 0) {
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "dash : onCuepoint_cont_scte35 : ts='%ui', type='%s', "\
+ "segupid='%f', segduration='%f', segrduration='%f'",
+ h->timestamp, v.mtype, v.segupid, v.segduration, v.segrduration);
+ } else {
+ ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ "dash : onCuepoint_cont_scte35 : amf not understood");
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_rtmp_dash_ad_markers(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
+ ngx_chain_t *in)
+{
+ ngx_rtmp_dash_app_conf_t *dacf;
+ ngx_rtmp_dash_ctx_t *ctx;
+
+ dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module);
+ if (dacf == NULL || !dacf->dash || dacf->path.len == 0) {
+ return NGX_OK;
+ }
+
+ ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module);
+
+ switch (dacf->ad_markers) {
+ case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT:
+ return ngx_rtmp_dash_on_cuepoint(s, h, in);
+ break;
+ case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35:
+ if (ctx->end_cuepoint)
+ return ngx_rtmp_dash_on_cuepoint_cont_scte35(s, h, in);
+ else
+ return ngx_rtmp_dash_on_cuepoint_scte35(s, h, in);
+ break;
+ default:
+ return NGX_OK;
+ }
+
+ return NGX_OK;
+}
+
+
static void *
ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf)
{
@@ -1662,7 +2742,13 @@ ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf)
conf->playlen = NGX_CONF_UNSET_MSEC;
conf->cleanup = NGX_CONF_UNSET;
conf->nested = NGX_CONF_UNSET;
+ conf->repetition = NGX_CONF_UNSET;
+ conf->cenc = NGX_CONF_UNSET;
+ conf->wdv = NGX_CONF_UNSET;
+ conf->mspr = NGX_CONF_UNSET;
conf->clock_compensation = NGX_CONF_UNSET;
+ conf->ad_markers = NGX_CONF_UNSET;
+ conf->ad_markers_timehack = NGX_CONF_UNSET;
return conf;
}
@@ -1680,9 +2766,22 @@ ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_msec_value(conf->playlen, prev->playlen, 30000);
ngx_conf_merge_value(conf->cleanup, prev->cleanup, 1);
ngx_conf_merge_value(conf->nested, prev->nested, 0);
+ ngx_conf_merge_value(conf->repetition, prev->repetition, 0);
+ ngx_conf_merge_value(conf->cenc, prev->cenc, 0);
+ ngx_conf_merge_str_value(conf->cenc_key, prev->cenc_key, "");
+ ngx_conf_merge_str_value(conf->cenc_kid, prev->cenc_kid, "");
+ ngx_conf_merge_value(conf->wdv, prev->wdv, 0);
+ ngx_conf_merge_str_value(conf->wdv_data, prev->wdv_data, "");
+ ngx_conf_merge_value(conf->mspr, prev->mspr, 0);
+ ngx_conf_merge_str_value(conf->mspr_data, prev->mspr_data, "");
+ ngx_conf_merge_str_value(conf->mspr_kid, prev->mspr_kid, "");
+ ngx_conf_merge_str_value(conf->mspr_pro, prev->mspr_pro, "");
ngx_conf_merge_uint_value(conf->clock_compensation, prev->clock_compensation,
NGX_RTMP_DASH_CLOCK_COMPENSATION_OFF);
ngx_conf_merge_str_value(conf->clock_helper_uri, prev->clock_helper_uri, "");
+ ngx_conf_merge_uint_value(conf->ad_markers, prev->ad_markers,
+ NGX_RTMP_DASH_AD_MARKERS_OFF);
+ ngx_conf_merge_value(conf->ad_markers_timehack, prev->ad_markers_timehack, 0);
if (conf->fraglen) {
conf->winfrags = conf->playlen / conf->fraglen;
@@ -1730,6 +2829,7 @@ ngx_rtmp_dash_postconfiguration(ngx_conf_t *cf)
{
ngx_rtmp_handler_pt *h;
ngx_rtmp_core_main_conf_t *cmcf;
+ ngx_rtmp_amf_handler_t *ch;
cmcf = ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_core_module);
@@ -1754,5 +2854,9 @@ ngx_rtmp_dash_postconfiguration(ngx_conf_t *cf)
next_playlist = ngx_rtmp_playlist;
ngx_rtmp_playlist = ngx_rtmp_dash_playlist;
+ ch = ngx_array_push(&cmcf->amf);
+ ngx_str_set(&ch->name, "onCuePoint");
+ ch->handler = ngx_rtmp_dash_ad_markers;
+
return NGX_OK;
}
diff --git a/dash/ngx_rtmp_dash_templates.h b/dash/ngx_rtmp_dash_templates.h
new file mode 100644
index 000000000..0e05d5902
--- /dev/null
+++ b/dash/ngx_rtmp_dash_templates.h
@@ -0,0 +1,195 @@
+#ifndef _NGX_RTMP_DASH_TEMPLATES
+#define _NGX_RTMP_DASH_TEMPLATES
+
+
+#define NGX_RTMP_DASH_MANIFEST_HEADER \
+ "\n" \
+ "\n"
+
+
+#define NGX_RTMP_DASH_MANIFEST_PERIOD \
+ " \n"
+
+
+#define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO \
+ " \n"
+
+
+#define NGX_RTMP_DASH_INBAND_EVENT \
+ " \n"
+
+
+#define NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_CENC \
+ " \n"
+
+
+#define NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC \
+ " \n" \
+ " %V\n" \
+ " \n"
+
+
+#define NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_WDV \
+ " \n" \
+ " %V\n" \
+ " \n"
+
+
+#define NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_MSPR \
+ " \n" \
+ " %V\n" \
+ " 1\n" \
+ " 8\n" \
+ " %V\n" \
+ " %V\n" \
+ " \n"
+
+
+#define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO \
+ " \n" \
+ " \n" \
+ " \n"
+
+
+#define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VARIANT_VIDEO \
+ " \n"
+
+
+#define NGX_RTMP_DASH_MANIFEST_SEGMENTTPL_VARIANT_VIDEO \
+ " \n" \
+ " \n"
+
+
+#define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO_FOOTER \
+ " \n" \
+ " \n" \
+ " \n"
+
+
+#define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO_FOOTER \
+ " \n"
+
+
+// " \n"
+
+#define NGX_RTMP_DASH_MANIFEST_TIME \
+ " \n"
+
+
+#define NGX_RTMP_DASH_MANIFEST_TIME_WITH_REPETITION \
+ " \n"
+
+
+#define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO \
+ " \n" \
+ " \n"
+
+
+#define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO \
+ " \n" \
+ " \n" \
+ " \n"
+
+
+#define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO_FOOTER \
+ " \n" \
+ " \n" \
+ " \n"
+
+
+#define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO_FOOTER \
+ " \n"
+
+
+#define NGX_RTMP_DASH_PERIOD_FOOTER \
+ " \n"
+
+
+#define NGX_RTMP_DASH_MANIFEST_CLOCK \
+ " \n"
+
+
+#define NGX_RTMP_DASH_MANIFEST_FOOTER \
+ "\n"
+
+
+#endif
diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c
index dd680c9cd..e1cf0fbc4 100644
--- a/dash/ngx_rtmp_mp4.c
+++ b/dash/ngx_rtmp_mp4.c
@@ -316,20 +316,27 @@ ngx_rtmp_mp4_write_tkhd(ngx_rtmp_session_t *s, ngx_buf_t *b,
ngx_rtmp_mp4_field_32(b, 0);
/* reserved */
- ngx_rtmp_mp4_field_16(b, ttype == NGX_RTMP_MP4_VIDEO_TRACK ? 0 : 0x0100);
+ if (ttype == NGX_RTMP_MP4_VIDEO_TRACK ||
+ ttype == NGX_RTMP_MP4_EVIDEO_TRACK) {
+ ngx_rtmp_mp4_field_16(b, 0);
+ } else {
+ /* reserved */
+ ngx_rtmp_mp4_field_16(b, 0x0100);
+ }
/* reserved */
ngx_rtmp_mp4_field_16(b, 0);
ngx_rtmp_mp4_write_matrix(b, 1, 0, 0, 1, 0, 0);
- if (ttype == NGX_RTMP_MP4_VIDEO_TRACK) {
+ if (ttype == NGX_RTMP_MP4_VIDEO_TRACK ||
+ ttype == NGX_RTMP_MP4_EVIDEO_TRACK) {
ngx_rtmp_mp4_field_32(b, (uint32_t) codec_ctx->width << 16);
ngx_rtmp_mp4_field_32(b, (uint32_t) codec_ctx->height << 16);
} else {
ngx_rtmp_mp4_field_32(b, 0);
ngx_rtmp_mp4_field_32(b, 0);
- }
+ }
ngx_rtmp_mp4_update_box_size(b, pos);
@@ -384,7 +391,8 @@ ngx_rtmp_mp4_write_hdlr(ngx_buf_t *b, ngx_rtmp_mp4_track_type_t ttype)
/* pre defined */
ngx_rtmp_mp4_field_32(b, 0);
- if (ttype == NGX_RTMP_MP4_VIDEO_TRACK) {
+ if (ttype == NGX_RTMP_MP4_VIDEO_TRACK ||
+ ttype == NGX_RTMP_MP4_EVIDEO_TRACK) {
ngx_rtmp_mp4_box(b, "vide");
} else {
ngx_rtmp_mp4_box(b, "soun");
@@ -395,7 +403,8 @@ ngx_rtmp_mp4_write_hdlr(ngx_buf_t *b, ngx_rtmp_mp4_track_type_t ttype)
ngx_rtmp_mp4_field_32(b, 0);
ngx_rtmp_mp4_field_32(b, 0);
- if (ttype == NGX_RTMP_MP4_VIDEO_TRACK) {
+ if (ttype == NGX_RTMP_MP4_VIDEO_TRACK ||
+ ttype == NGX_RTMP_MP4_EVIDEO_TRACK) {
/* video handler string, NULL-terminated */
ngx_rtmp_mp4_data(b, "VideoHandler", sizeof("VideoHandler"));
} else {
@@ -489,6 +498,216 @@ ngx_rtmp_mp4_write_dinf(ngx_buf_t *b)
}
+static ngx_int_t
+ngx_rtmp_mp4_write_frma(ngx_buf_t *b, const char format[4])
+{
+ u_char *pos;
+
+ pos = ngx_rtmp_mp4_start_box(b, "frma");
+
+ /* original_format */
+ ngx_rtmp_mp4_box(b, format);
+
+ ngx_rtmp_mp4_update_box_size(b, pos);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_rtmp_mp4_write_schm(ngx_buf_t *b)
+{
+ u_char *pos;
+
+ pos = ngx_rtmp_mp4_start_box(b, "schm");
+
+ /* version and flags */
+ ngx_rtmp_mp4_field_32(b, 0);
+
+ /* scheme_type */
+ ngx_rtmp_mp4_box(b, "cenc");
+
+ /* scheme_version */
+ ngx_rtmp_mp4_field_32(b, 65536);
+
+ ngx_rtmp_mp4_update_box_size(b, pos);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_rtmp_mp4_write_pssh_cenc(ngx_buf_t *b,
+ ngx_rtmp_cenc_drm_info_t *drmi)
+{
+ u_char *pos;
+ u_char sid[] = {
+ 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
+ 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b
+ };
+
+ pos = ngx_rtmp_mp4_start_box(b, "pssh");
+
+ /* version and flags */
+ ngx_rtmp_mp4_field_32(b, 0x01000000);
+
+ /* system ID : org.w3.clearkey */
+ ngx_rtmp_mp4_data(b, sid, NGX_RTMP_CENC_KEY_SIZE);
+
+ /* kid count */
+ ngx_rtmp_mp4_field_32(b, 1);
+
+ /* default KID */
+ ngx_rtmp_mp4_data(b, drmi->kid, NGX_RTMP_CENC_KEY_SIZE);
+
+ /* data size */
+ ngx_rtmp_mp4_field_32(b, 0);
+
+ ngx_rtmp_mp4_update_box_size(b, pos);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_rtmp_mp4_write_pssh_wdv(ngx_buf_t *b,
+ ngx_rtmp_cenc_drm_info_t *drmi)
+{
+ ngx_str_t dest, src;
+ u_char *pos;
+ u_char buf[NGX_RTMP_CENC_MAX_PSSH_SIZE];
+
+ u_char sid[] = {
+ 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce,
+ 0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed
+ };
+
+ pos = ngx_rtmp_mp4_start_box(b, "pssh");
+
+ /* assuming v0 pssh for widevine */
+ ngx_rtmp_mp4_field_32(b, 0);
+
+ /* system ID : com.widevine.alpha */
+ ngx_rtmp_mp4_data(b, sid, NGX_RTMP_CENC_KEY_SIZE);
+
+ /* decode base64 wdv_data */
+ dest.data = buf;
+ src.len = ngx_base64_decoded_length(drmi->wdv_data.len) - 32;
+ ngx_decode_base64(&dest, &drmi->wdv_data);
+
+ /* data size */
+ ngx_rtmp_mp4_field_32(b, src.len);
+
+ /* data */
+ ngx_rtmp_mp4_data(b, dest.data + 32, src.len);
+
+ ngx_rtmp_mp4_update_box_size(b, pos);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_rtmp_mp4_write_pssh_mspr(ngx_buf_t *b,
+ ngx_rtmp_cenc_drm_info_t *drmi)
+{
+ ngx_str_t dest, src;
+ u_char *pos;
+ u_char buf[NGX_RTMP_CENC_MAX_PSSH_SIZE];
+
+ u_char sid[] = {
+ 0x9a, 0x04, 0xf0, 0x79, 0x98, 0x40, 0x42, 0x86,
+ 0xab, 0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95
+ };
+
+ pos = ngx_rtmp_mp4_start_box(b, "pssh");
+
+ /* assuming v0 pssh for playready */
+ ngx_rtmp_mp4_field_32(b, 0);
+
+ /* system ID : com.microsoft.playready */
+ ngx_rtmp_mp4_data(b, sid, NGX_RTMP_CENC_KEY_SIZE);
+
+ /* decode base64 mspr_data */
+ dest.data = buf;
+ src.len = ngx_base64_decoded_length(drmi->mspr_data.len) - 32;
+ ngx_decode_base64(&dest, &drmi->mspr_data);
+
+ /* data size */
+ ngx_rtmp_mp4_field_32(b, src.len);
+
+ /* data */
+ ngx_rtmp_mp4_data(b, dest.data + 32, src.len);
+
+ ngx_rtmp_mp4_update_box_size(b, pos);
+
+ return NGX_OK;
+}
+
+
+
+static ngx_int_t
+ngx_rtmp_mp4_write_tenc(ngx_buf_t *b, ngx_rtmp_cenc_drm_info_t *drmi)
+{
+ u_char *pos;
+
+ pos = ngx_rtmp_mp4_start_box(b, "tenc");
+
+ /* version and flags */
+ ngx_rtmp_mp4_field_32(b, 0);
+
+ /* reserved */
+ ngx_rtmp_mp4_field_8(b, 0);
+ ngx_rtmp_mp4_field_8(b, 0);
+
+ /* default isProtected */
+ ngx_rtmp_mp4_field_8(b, 1);
+
+ /* default per_sample_iv_size */
+ ngx_rtmp_mp4_field_8(b, NGX_RTMP_CENC_IV_SIZE);
+
+ /* default KID */
+ ngx_rtmp_mp4_data(b, drmi->kid, NGX_RTMP_CENC_KEY_SIZE);
+
+ ngx_rtmp_mp4_update_box_size(b, pos);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_rtmp_mp4_write_schi(ngx_buf_t *b, ngx_rtmp_cenc_drm_info_t *drmi)
+{
+ u_char *pos;
+
+ pos = ngx_rtmp_mp4_start_box(b, "schi");
+
+ ngx_rtmp_mp4_write_tenc(b, drmi);
+
+ ngx_rtmp_mp4_update_box_size(b, pos);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_rtmp_mp4_write_sinf(ngx_buf_t *b,
+ const char format[4], ngx_rtmp_cenc_drm_info_t *drmi)
+{
+ u_char *pos;
+
+ pos = ngx_rtmp_mp4_start_box(b, "sinf");
+
+ ngx_rtmp_mp4_write_frma(b, format);
+ ngx_rtmp_mp4_write_schm(b);
+ ngx_rtmp_mp4_write_schi(b, drmi);
+
+ ngx_rtmp_mp4_update_box_size(b, pos);
+
+ return NGX_OK;
+}
+
+
static ngx_int_t
ngx_rtmp_mp4_write_avcc(ngx_rtmp_session_t *s, ngx_buf_t *b)
{
@@ -597,6 +816,71 @@ ngx_rtmp_mp4_write_video(ngx_rtmp_session_t *s, ngx_buf_t *b)
}
+static ngx_int_t
+ngx_rtmp_mp4_write_evideo(ngx_rtmp_session_t *s,
+ ngx_buf_t *b, ngx_rtmp_cenc_drm_info_t *drmi)
+{
+ u_char *pos;
+ ngx_rtmp_codec_ctx_t *codec_ctx;
+
+ codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
+
+ pos = ngx_rtmp_mp4_start_box(b, "encv");
+
+ /* reserved */
+ ngx_rtmp_mp4_field_32(b, 0);
+ ngx_rtmp_mp4_field_16(b, 0);
+
+ /* data reference index */
+ ngx_rtmp_mp4_field_16(b, 1);
+
+ /* codec stream version & revision */
+ ngx_rtmp_mp4_field_16(b, 0);
+ ngx_rtmp_mp4_field_16(b, 0);
+
+ /* reserved */
+ ngx_rtmp_mp4_field_32(b, 0);
+ ngx_rtmp_mp4_field_32(b, 0);
+ ngx_rtmp_mp4_field_32(b, 0);
+
+ /* width & height */
+ ngx_rtmp_mp4_field_16(b, (uint16_t) codec_ctx->width);
+ ngx_rtmp_mp4_field_16(b, (uint16_t) codec_ctx->height);
+
+ /* horizontal & vertical resolutions 72 dpi */
+ ngx_rtmp_mp4_field_32(b, 0x00480000);
+ ngx_rtmp_mp4_field_32(b, 0x00480000);
+
+ /* data size */
+ ngx_rtmp_mp4_field_32(b, 0);
+
+ /* frame count */
+ ngx_rtmp_mp4_field_16(b, 1);
+
+ /* compressor name */
+ ngx_rtmp_mp4_field_32(b, 0);
+ ngx_rtmp_mp4_field_32(b, 0);
+ ngx_rtmp_mp4_field_32(b, 0);
+
+ /* reserved */
+ ngx_rtmp_mp4_field_32(b, 0);
+ ngx_rtmp_mp4_field_32(b, 0);
+ ngx_rtmp_mp4_field_32(b, 0);
+ ngx_rtmp_mp4_field_32(b, 0);
+ ngx_rtmp_mp4_field_32(b, 0);
+ ngx_rtmp_mp4_field_16(b, 0x18);
+ ngx_rtmp_mp4_field_16(b, 0xffff);
+
+ ngx_rtmp_mp4_write_avcc(s, b);
+
+ ngx_rtmp_mp4_write_sinf(b, "avc1", drmi);
+
+ ngx_rtmp_mp4_update_box_size(b, pos);
+
+ return NGX_OK;
+}
+
+
static ngx_int_t
ngx_rtmp_mp4_write_esds(ngx_rtmp_session_t *s, ngx_buf_t *b)
{
@@ -727,9 +1011,63 @@ ngx_rtmp_mp4_write_audio(ngx_rtmp_session_t *s, ngx_buf_t *b)
}
+static ngx_int_t
+ngx_rtmp_mp4_write_eaudio(ngx_rtmp_session_t *s,
+ ngx_buf_t *b, ngx_rtmp_cenc_drm_info_t *drmi)
+{
+ u_char *pos;
+ ngx_rtmp_codec_ctx_t *codec_ctx;
+
+ codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
+
+ pos = ngx_rtmp_mp4_start_box(b, "enca");
+
+ /* reserved */
+ ngx_rtmp_mp4_field_32(b, 0);
+ ngx_rtmp_mp4_field_16(b, 0);
+
+ /* data reference index */
+ ngx_rtmp_mp4_field_16(b, 1);
+
+ /* reserved */
+ ngx_rtmp_mp4_field_32(b, 0);
+ ngx_rtmp_mp4_field_32(b, 0);
+
+ /* channel count */
+ ngx_rtmp_mp4_field_16(b, (uint16_t) codec_ctx->audio_channels);
+
+ /* sample size */
+ ngx_rtmp_mp4_field_16(b, (uint16_t) (codec_ctx->sample_size * 8));
+
+ /* reserved */
+ ngx_rtmp_mp4_field_32(b, 0);
+
+ /* time scale */
+ ngx_rtmp_mp4_field_16(b, 1000);
+
+ /* sample rate */
+ ngx_rtmp_mp4_field_16(b, (uint16_t) codec_ctx->sample_rate);
+
+ ngx_rtmp_mp4_write_esds(s, b);
+#if 0
+ /* tag size*/
+ ngx_rtmp_mp4_field_32(b, 8);
+
+ /* null tag */
+ ngx_rtmp_mp4_field_32(b, 0);
+#endif
+
+ ngx_rtmp_mp4_write_sinf(b, "mp4a", drmi);
+
+ ngx_rtmp_mp4_update_box_size(b, pos);
+
+ return NGX_OK;
+}
+
+
static ngx_int_t
ngx_rtmp_mp4_write_stsd(ngx_rtmp_session_t *s, ngx_buf_t *b,
- ngx_rtmp_mp4_track_type_t ttype)
+ ngx_rtmp_mp4_track_type_t ttype, ngx_rtmp_cenc_drm_info_t *drmi)
{
u_char *pos;
@@ -741,10 +1079,14 @@ ngx_rtmp_mp4_write_stsd(ngx_rtmp_session_t *s, ngx_buf_t *b,
/* entry count */
ngx_rtmp_mp4_field_32(b, 1);
- if (ttype == NGX_RTMP_MP4_VIDEO_TRACK) {
+ if (ttype == NGX_RTMP_MP4_VIDEO_TRACK) {
ngx_rtmp_mp4_write_video(s, b);
- } else {
+ } else if (ttype == NGX_RTMP_MP4_EVIDEO_TRACK){
+ ngx_rtmp_mp4_write_evideo(s, b, drmi);
+ } else if (ttype == NGX_RTMP_MP4_AUDIO_TRACK){
ngx_rtmp_mp4_write_audio(s, b);
+ } else if (ttype == NGX_RTMP_MP4_EAUDIO_TRACK){
+ ngx_rtmp_mp4_write_eaudio(s, b, drmi);
}
ngx_rtmp_mp4_update_box_size(b, pos);
@@ -820,13 +1162,13 @@ ngx_rtmp_mp4_write_stco(ngx_buf_t *b)
static ngx_int_t
ngx_rtmp_mp4_write_stbl(ngx_rtmp_session_t *s, ngx_buf_t *b,
- ngx_rtmp_mp4_track_type_t ttype)
+ ngx_rtmp_mp4_track_type_t ttype, ngx_rtmp_cenc_drm_info_t *drmi)
{
u_char *pos;
pos = ngx_rtmp_mp4_start_box(b, "stbl");
- ngx_rtmp_mp4_write_stsd(s, b, ttype);
+ ngx_rtmp_mp4_write_stsd(s, b, ttype, drmi);
ngx_rtmp_mp4_write_stts(b);
ngx_rtmp_mp4_write_stsc(b);
ngx_rtmp_mp4_write_stsz(b);
@@ -840,20 +1182,21 @@ ngx_rtmp_mp4_write_stbl(ngx_rtmp_session_t *s, ngx_buf_t *b,
static ngx_int_t
ngx_rtmp_mp4_write_minf(ngx_rtmp_session_t *s, ngx_buf_t *b,
- ngx_rtmp_mp4_track_type_t ttype)
+ ngx_rtmp_mp4_track_type_t ttype, ngx_rtmp_cenc_drm_info_t *drmi)
{
u_char *pos;
pos = ngx_rtmp_mp4_start_box(b, "minf");
- if (ttype == NGX_RTMP_MP4_VIDEO_TRACK) {
+ if (ttype == NGX_RTMP_MP4_VIDEO_TRACK ||
+ ttype == NGX_RTMP_MP4_EVIDEO_TRACK) {
ngx_rtmp_mp4_write_vmhd(b);
} else {
ngx_rtmp_mp4_write_smhd(b);
}
ngx_rtmp_mp4_write_dinf(b);
- ngx_rtmp_mp4_write_stbl(s, b, ttype);
+ ngx_rtmp_mp4_write_stbl(s, b, ttype, drmi);
ngx_rtmp_mp4_update_box_size(b, pos);
@@ -863,7 +1206,7 @@ ngx_rtmp_mp4_write_minf(ngx_rtmp_session_t *s, ngx_buf_t *b,
static ngx_int_t
ngx_rtmp_mp4_write_mdia(ngx_rtmp_session_t *s, ngx_buf_t *b,
- ngx_rtmp_mp4_track_type_t ttype)
+ ngx_rtmp_mp4_track_type_t ttype, ngx_rtmp_cenc_drm_info_t *drmi)
{
u_char *pos;
@@ -871,7 +1214,7 @@ ngx_rtmp_mp4_write_mdia(ngx_rtmp_session_t *s, ngx_buf_t *b,
ngx_rtmp_mp4_write_mdhd(b);
ngx_rtmp_mp4_write_hdlr(b, ttype);
- ngx_rtmp_mp4_write_minf(s, b, ttype);
+ ngx_rtmp_mp4_write_minf(s, b, ttype, drmi);
ngx_rtmp_mp4_update_box_size(b, pos);
@@ -880,14 +1223,14 @@ ngx_rtmp_mp4_write_mdia(ngx_rtmp_session_t *s, ngx_buf_t *b,
static ngx_int_t
ngx_rtmp_mp4_write_trak(ngx_rtmp_session_t *s, ngx_buf_t *b,
- ngx_rtmp_mp4_track_type_t ttype)
+ ngx_rtmp_mp4_track_type_t ttype, ngx_rtmp_cenc_drm_info_t *drmi)
{
u_char *pos;
pos = ngx_rtmp_mp4_start_box(b, "trak");
ngx_rtmp_mp4_write_tkhd(s, b, ttype);
- ngx_rtmp_mp4_write_mdia(s, b, ttype);
+ ngx_rtmp_mp4_write_mdia(s, b, ttype, drmi);
ngx_rtmp_mp4_update_box_size(b, pos);
@@ -932,7 +1275,7 @@ ngx_rtmp_mp4_write_mvex(ngx_buf_t *b)
ngx_int_t
ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b,
- ngx_rtmp_mp4_track_type_t ttype)
+ ngx_rtmp_mp4_track_type_t ttype, ngx_rtmp_cenc_drm_info_t *drmi)
{
u_char *pos;
@@ -940,7 +1283,18 @@ ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b,
ngx_rtmp_mp4_write_mvhd(b);
ngx_rtmp_mp4_write_mvex(b);
- ngx_rtmp_mp4_write_trak(s, b, ttype);
+ ngx_rtmp_mp4_write_trak(s, b, ttype, drmi);
+
+ if (ttype == NGX_RTMP_MP4_EVIDEO_TRACK ||
+ ttype == NGX_RTMP_MP4_EAUDIO_TRACK) {
+ ngx_rtmp_mp4_write_pssh_cenc(b, drmi);
+ if (drmi->wdv) {
+ ngx_rtmp_mp4_write_pssh_wdv(b, drmi);
+ }
+ if (drmi->mspr) {
+ ngx_rtmp_mp4_write_pssh_mspr(b, drmi);
+ }
+ }
ngx_rtmp_mp4_update_box_size(b, pos);
@@ -985,8 +1339,9 @@ ngx_rtmp_mp4_write_tfdt(ngx_buf_t *b, uint32_t earliest_pres_time)
static ngx_int_t
-ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count,
- ngx_rtmp_mp4_sample_t *samples, ngx_uint_t sample_mask, u_char *moof_pos)
+ngx_rtmp_mp4_write_trun(ngx_buf_t *b, char type,
+ uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples,
+ ngx_uint_t sample_mask, u_char *moof_pos, ngx_flag_t is_protected)
{
u_char *pos;
uint32_t i, offset, nitems, flags;
@@ -1018,7 +1373,20 @@ ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count,
flags |= 0x000800;
}
- offset = (pos - moof_pos) + 20 + (sample_count * nitems * 4) + 8;
+ if (is_protected) {
+ /* if cenc is enabled we neeed to add
+ * saiz saiz senc size to the data offset */
+ offset = (pos - moof_pos) + 20 + (sample_count * nitems * 4);
+ if (type == 'v') {
+ /* video use sub sample senc */
+ offset += 17 + 20 + 16 + (sample_count * (NGX_RTMP_CENC_IV_SIZE + 8)) + 8;
+ } else {
+ /* audio use full sample senc */
+ offset += 17 + 20 + 16 + (sample_count * NGX_RTMP_CENC_IV_SIZE) + 8;
+ }
+ } else {
+ offset = (pos - moof_pos) + 20 + (sample_count * nitems * 4) + 8;
+ }
ngx_rtmp_mp4_field_32(b, flags);
ngx_rtmp_mp4_field_32(b, sample_count);
@@ -1049,10 +1417,109 @@ ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count,
}
+static ngx_int_t
+ngx_rtmp_mp4_write_saiz(ngx_buf_t *b, char type, uint32_t sample_count)
+{
+ u_char *pos;
+
+ pos = ngx_rtmp_mp4_start_box(b, "saiz");
+
+ /* version & flag */
+ ngx_rtmp_mp4_field_32(b, 0);
+
+ /* defaut sample info size */
+ if (type == 'v') {
+ /* sub sample */
+ ngx_rtmp_mp4_field_8(b, NGX_RTMP_CENC_IV_SIZE + 8);
+ } else {
+ /* full sample */
+ ngx_rtmp_mp4_field_8(b, NGX_RTMP_CENC_IV_SIZE);
+ }
+
+ /* sample count */
+ ngx_rtmp_mp4_field_32(b, sample_count);
+
+ ngx_rtmp_mp4_update_box_size(b, pos);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_rtmp_mp4_write_saio(ngx_buf_t *b, u_char *moof_pos)
+{
+ u_char *pos;
+ uint32_t offset;
+
+ pos = ngx_rtmp_mp4_start_box(b, "saio");
+
+ /* version & flag */
+ ngx_rtmp_mp4_field_32(b, 0);
+
+ /* entry count */
+ ngx_rtmp_mp4_field_32(b, 1);
+
+ /* entry 0 is offset to the first IV in senc box */
+ offset = (pos - moof_pos) + 20 + 16;
+ ngx_rtmp_mp4_field_32(b, offset);
+
+ ngx_rtmp_mp4_update_box_size(b, pos);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_rtmp_mp4_write_senc(ngx_buf_t *b, char type,
+ uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples)
+{
+ u_char *pos;
+ uint32_t i;
+
+ pos = ngx_rtmp_mp4_start_box(b, "senc");
+
+ /* version & flag */
+ if (type == 'v') {
+ /* video use sub_sample flag 0x02 */
+ ngx_rtmp_mp4_field_32(b, 0x02);
+ } else {
+ /* audio use full_sample flag 0x00 */
+ ngx_rtmp_mp4_field_32(b, 0);
+ }
+
+ /* sample count */
+ ngx_rtmp_mp4_field_32(b, sample_count);
+
+ for (i = 0; i < sample_count; i++, samples++) {
+
+ /* IV per sample */
+ ngx_rtmp_mp4_data(b, samples->iv, NGX_RTMP_CENC_IV_SIZE);
+
+ /* subsample informations */
+ if (type == 'v') {
+
+ /* sub sample count */
+ ngx_rtmp_mp4_field_16(b, 1);
+
+ /* sub sample clear data size */
+ ngx_rtmp_mp4_field_16(b, samples->clear_size);
+
+ /* sub sample protected data size */
+ ngx_rtmp_mp4_field_32(b, samples->size - samples->clear_size);
+
+ }
+ }
+
+ ngx_rtmp_mp4_update_box_size(b, pos);
+
+ return NGX_OK;
+}
+
+
static ngx_int_t
ngx_rtmp_mp4_write_traf(ngx_buf_t *b, uint32_t earliest_pres_time,
- uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples,
- ngx_uint_t sample_mask, u_char *moof_pos)
+ char type, uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples,
+ ngx_uint_t sample_mask, u_char *moof_pos, ngx_flag_t is_protected)
{
u_char *pos;
@@ -1060,7 +1527,14 @@ ngx_rtmp_mp4_write_traf(ngx_buf_t *b, uint32_t earliest_pres_time,
ngx_rtmp_mp4_write_tfhd(b);
ngx_rtmp_mp4_write_tfdt(b, earliest_pres_time);
- ngx_rtmp_mp4_write_trun(b, sample_count, samples, sample_mask, moof_pos);
+ ngx_rtmp_mp4_write_trun(b, type, sample_count, samples, sample_mask,
+ moof_pos, is_protected);
+
+ if (is_protected) {
+ ngx_rtmp_mp4_write_saiz(b, type, sample_count);
+ ngx_rtmp_mp4_write_saio(b, moof_pos);
+ ngx_rtmp_mp4_write_senc(b, type, sample_count, samples);
+ }
ngx_rtmp_mp4_update_box_size(b, pos);
@@ -1139,16 +1613,16 @@ ngx_rtmp_mp4_write_sidx(ngx_buf_t *b, ngx_uint_t reference_size,
ngx_int_t
ngx_rtmp_mp4_write_moof(ngx_buf_t *b, uint32_t earliest_pres_time,
- uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples,
- ngx_uint_t sample_mask, uint32_t index)
+ char type, uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples,
+ ngx_uint_t sample_mask, uint32_t index, ngx_flag_t is_protected)
{
u_char *pos;
pos = ngx_rtmp_mp4_start_box(b, "moof");
ngx_rtmp_mp4_write_mfhd(b, index);
- ngx_rtmp_mp4_write_traf(b, earliest_pres_time, sample_count, samples,
- sample_mask, pos);
+ ngx_rtmp_mp4_write_traf(b, earliest_pres_time, type, sample_count,
+ samples, sample_mask, pos, is_protected);
ngx_rtmp_mp4_update_box_size(b, pos);
@@ -1156,7 +1630,7 @@ ngx_rtmp_mp4_write_moof(ngx_buf_t *b, uint32_t earliest_pres_time,
}
-ngx_uint_t
+ngx_int_t
ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size)
{
ngx_rtmp_mp4_field_32(b, size);
@@ -1165,3 +1639,53 @@ ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size)
return NGX_OK;
}
+
+ngx_int_t
+ngx_rtmp_mp4_write_emsg(ngx_buf_t *b,
+ uint32_t earliest_pres_time, uint32_t cuepoint_time, uint32_t duration_time, uint32_t id)
+{
+ u_char *pos;
+ uint32_t delta_time;
+ uint32_t timescale = 1000;
+
+ delta_time = cuepoint_time - earliest_pres_time;
+
+ pos = ngx_rtmp_mp4_start_box(b, "emsg");
+
+ /* version & flag */
+ ngx_rtmp_mp4_field_32(b, 0);
+
+ /* scheme_id_uri */
+ ngx_rtmp_mp4_data(b, "urn:scte:scte35:2013:xml", sizeof("urn:scte:scte35:2013:xml"));
+
+ /* value */
+ ngx_rtmp_mp4_data(b, "1", sizeof("1"));
+
+ /* timescale */
+ ngx_rtmp_mp4_field_32(b, timescale);
+
+ /* presentation_time_delta */
+ ngx_rtmp_mp4_field_32(b, delta_time);
+
+ /* duration */
+ ngx_rtmp_mp4_field_32(b, duration_time);
+
+ /* id */
+ ngx_rtmp_mp4_field_32(b, id);
+
+#define SCTE_EVENT "\
+ \
+ \
+ \
+"
+
+ /* data */
+ ngx_rtmp_mp4_data(b, SCTE_EVENT, sizeof(SCTE_EVENT));
+
+ ngx_rtmp_mp4_update_box_size(b, pos);
+
+ return NGX_OK;
+
+}
+
diff --git a/dash/ngx_rtmp_mp4.h b/dash/ngx_rtmp_mp4.h
index 697b6c87c..455cd1040 100644
--- a/dash/ngx_rtmp_mp4.h
+++ b/dash/ngx_rtmp_mp4.h
@@ -7,6 +7,7 @@
#include
#include
#include
+#include "ngx_rtmp_cenc.h"
#define NGX_RTMP_MP4_SAMPLE_SIZE 0x01
@@ -17,10 +18,13 @@
typedef struct {
uint32_t size;
+ uint32_t clear_size;
uint32_t duration;
uint32_t delay;
uint32_t timestamp;
unsigned key:1;
+ unsigned is_protected:1;
+ u_char iv[NGX_RTMP_CENC_IV_SIZE];
} ngx_rtmp_mp4_sample_t;
@@ -32,21 +36,24 @@ typedef enum {
typedef enum {
NGX_RTMP_MP4_VIDEO_TRACK,
- NGX_RTMP_MP4_AUDIO_TRACK
+ NGX_RTMP_MP4_AUDIO_TRACK,
+ NGX_RTMP_MP4_EVIDEO_TRACK,
+ NGX_RTMP_MP4_EAUDIO_TRACK
} ngx_rtmp_mp4_track_type_t;
ngx_int_t ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b);
ngx_int_t ngx_rtmp_mp4_write_styp(ngx_buf_t *b);
ngx_int_t ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b,
- ngx_rtmp_mp4_track_type_t ttype);
+ ngx_rtmp_mp4_track_type_t ttype, ngx_rtmp_cenc_drm_info_t *drmi);
ngx_int_t ngx_rtmp_mp4_write_moof(ngx_buf_t *b, uint32_t earliest_pres_time,
- uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples,
- ngx_uint_t sample_mask, uint32_t index);
+ char type, uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples,
+ ngx_uint_t sample_mask, uint32_t index, ngx_flag_t is_protected);
ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_buf_t *b,
ngx_uint_t reference_size, uint32_t earliest_pres_time,
uint32_t latest_pres_time);
-ngx_uint_t ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size);
-
+ngx_int_t ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size);
+ngx_int_t ngx_rtmp_mp4_write_emsg(ngx_buf_t *b,
+ uint32_t earliest_pres_time, uint32_t cuepoint_time, uint32_t duration_time, uint32_t id);
#endif /* _NGX_RTMP_MP4_H_INCLUDED_ */
diff --git a/hls/ngx_rtmp_hls_module.c b/hls/ngx_rtmp_hls_module.c
index fbf2bcbf3..ff2b0f7b3 100644
--- a/hls/ngx_rtmp_hls_module.c
+++ b/hls/ngx_rtmp_hls_module.c
@@ -296,7 +296,7 @@ static ngx_command_t ngx_rtmp_hls_commands[] = {
ngx_conf_set_enum_slot,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_rtmp_hls_app_conf_t, allow_client_cache),
- &ngx_rtmp_hls_cache },
+ &ngx_rtmp_hls_cache },
{ ngx_string("hls_variant"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_1MORE,
@@ -816,7 +816,7 @@ ngx_rtmp_hls_append_sps_pps(ngx_rtmp_session_t *s, ngx_buf_t *out)
return NGX_ERROR;
}
- ngx_rtmp_rmemcpy(&len, &rlen, 2);
+ len=ntohs(rlen);
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"hls: header NAL length: %uz", (size_t) len);
@@ -2072,7 +2072,21 @@ ngx_rtmp_hls_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
}
len = 0;
- ngx_rtmp_rmemcpy(&len, &rlen, nal_bytes);
+
+ switch (nal_bytes) {
+ case 1:
+ len=*(uint8_t*)&rlen;
+ break;
+ case 2:
+ len=ntohs(*(uint16_t*)&rlen);
+ break;
+ case 3:
+ len=n3toh4((u_char*)&rlen);
+ break;
+ case 4:
+ len=ntohl(rlen);
+ break;
+ };
if (len == 0) {
continue;
diff --git a/ngx_rtmp.c b/ngx_rtmp.c
index ad671d475..e138f1209 100644
--- a/ngx_rtmp.c
+++ b/ngx_rtmp.c
@@ -825,22 +825,6 @@ ngx_rtmp_fire_event(ngx_rtmp_session_t *s, ngx_uint_t evt,
}
-void *
-ngx_rtmp_rmemcpy(void *dst, const void* src, size_t n)
-{
- u_char *d, *s;
-
- d = dst;
- s = (u_char*)src + n - 1;
-
- while(s >= (u_char*)src) {
- *d++ = *s--;
- }
-
- return dst;
-}
-
-
static ngx_int_t
ngx_rtmp_init_process(ngx_cycle_t *cycle)
{
diff --git a/ngx_rtmp.h b/ngx_rtmp.h
index f3a3d6f18..e88de4380 100644
--- a/ngx_rtmp.h
+++ b/ngx_rtmp.h
@@ -417,34 +417,33 @@ ngx_int_t ngx_rtmp_fire_event(ngx_rtmp_session_t *s, ngx_uint_t evt,
ngx_int_t ngx_rtmp_set_chunk_size(ngx_rtmp_session_t *s, ngx_uint_t size);
-/* Bit reverse: we need big-endians in many places */
-void * ngx_rtmp_rmemcpy(void *dst, const void* src, size_t n);
-
-#define ngx_rtmp_rcpymem(dst, src, n) \
- (((u_char*)ngx_rtmp_rmemcpy(dst, src, n)) + (n))
-
-
-static ngx_inline uint16_t
-ngx_rtmp_r16(uint16_t n)
+/* Bit agnosticism: we need network to host byte-order conversion in many places */
+static ngx_inline uint64_t
+ntohll(uint64_t n)
{
- return (n << 8) | (n >> 8);
+#if (NGX_HAVE_LITTLE_ENDIAN)
+ return (uint64_t) ntohl((uint32_t) n) << 32 |
+ ntohl((uint32_t) (n >> 32));
+#else
+ return n;
+#endif
}
-
static ngx_inline uint32_t
-ngx_rtmp_r32(uint32_t n)
+n3toh4(u_char* src)
{
- return (n << 24) | ((n << 8) & 0xff0000) | ((n >> 8) & 0xff00) | (n >> 24);
+ return ((uint32_t)src[0]<<16)|((uint32_t)src[1]<<8)|src[2];
}
-
-static ngx_inline uint64_t
-ngx_rtmp_r64(uint64_t n)
+static ngx_inline u_char*
+h4ton3(u_char* dst, uint32_t src)
{
- return (uint64_t) ngx_rtmp_r32((uint32_t) n) << 32 |
- ngx_rtmp_r32((uint32_t) (n >> 32));
-}
+ dst[0]=(u_char)(src>>16);
+ dst[1]=(u_char)(src>>8);
+ dst[2]=(u_char)src;
+ return dst+3;
+}
/* Receiving messages */
ngx_int_t ngx_rtmp_receive_message(ngx_rtmp_session_t *s,
diff --git a/ngx_rtmp_amf.c b/ngx_rtmp_amf.c
index b904651dc..465dc01cb 100644
--- a/ngx_rtmp_amf.c
+++ b/ngx_rtmp_amf.c
@@ -10,23 +10,6 @@
#include "ngx_rtmp.h"
#include
-
-static ngx_inline void*
-ngx_rtmp_amf_reverse_copy(void *dst, void* src, size_t len)
-{
- size_t k;
-
- if (dst == NULL || src == NULL) {
- return NULL;
- }
-
- for(k = 0; k < len; ++k) {
- ((u_char*)dst)[k] = ((u_char*)src)[len - 1 - k];
- }
-
- return dst;
-}
-
#define NGX_RTMP_AMF_DEBUG_SIZE 72
#ifdef NGX_DEBUG
@@ -207,7 +190,7 @@ ngx_rtmp_amf_read_object(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
return NGX_ERROR;
}
- ngx_rtmp_amf_reverse_copy(&len, buf, 2);
+ len=ntohs(*(uint16_t*)&buf[0]);
if (!len)
break;
@@ -258,7 +241,7 @@ ngx_rtmp_amf_read_array(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
if (ngx_rtmp_amf_get(ctx, buf, 4) != NGX_OK)
return NGX_ERROR;
- ngx_rtmp_amf_reverse_copy(&len, buf, 4);
+ len=ntohl(*(uint32_t*)&buf[0]);
for (n = 0; n < len; ++n) {
if (ngx_rtmp_amf_read(ctx, n < nelts ? &elts[n] : NULL, 1) != NGX_OK)
@@ -352,10 +335,9 @@ ngx_rtmp_amf_read(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
switch (type) {
case NGX_RTMP_AMF_NUMBER:
- if (ngx_rtmp_amf_get(ctx, buf, 8) != NGX_OK) {
+ if (ngx_rtmp_amf_get(ctx, data, 8) != NGX_OK) {
return NGX_ERROR;
}
- ngx_rtmp_amf_reverse_copy(data, buf, 8);
break;
case NGX_RTMP_AMF_BOOLEAN:
@@ -368,7 +350,7 @@ ngx_rtmp_amf_read(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
if (ngx_rtmp_amf_get(ctx, buf, 2) != NGX_OK) {
return NGX_ERROR;
}
- ngx_rtmp_amf_reverse_copy(&len, buf, 2);
+ len=ntohs(*(uint16_t*)buf);
if (data == NULL) {
rc = ngx_rtmp_amf_get(ctx, data, len);
@@ -438,14 +420,14 @@ ngx_rtmp_amf_read(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
if (ngx_rtmp_amf_get(ctx, buf, 2) != NGX_OK) {
return NGX_ERROR;
}
- ngx_rtmp_amf_reverse_copy(data, buf, 2);
+ *(uint16_t*)data=ntohs(*(uint16_t*)buf);
break;
case NGX_RTMP_AMF_INT32:
if (ngx_rtmp_amf_get(ctx, buf, 4) != NGX_OK) {
return NGX_ERROR;
}
- ngx_rtmp_amf_reverse_copy(data, buf, 4);
+ *(uint32_t*)data=ntohs(*(uint32_t*)buf);
break;
case NGX_RTMP_AMF_END:
@@ -476,9 +458,8 @@ ngx_rtmp_amf_write_object(ngx_rtmp_amf_ctx_t *ctx,
len = (uint16_t) elts[n].name.len;
- if (ngx_rtmp_amf_put(ctx,
- ngx_rtmp_amf_reverse_copy(buf,
- &len, 2), 2) != NGX_OK)
+ *(uint16_t*)buf = htons(len);
+ if (ngx_rtmp_amf_put(ctx, buf, 2) != NGX_OK)
{
return NGX_ERROR;
}
@@ -509,9 +490,8 @@ ngx_rtmp_amf_write_array(ngx_rtmp_amf_ctx_t *ctx,
u_char buf[4];
len = nelts;
- if (ngx_rtmp_amf_put(ctx,
- ngx_rtmp_amf_reverse_copy(buf,
- &len, 4), 4) != NGX_OK)
+ *(uint32_t*)buf = htonl(len);
+ if (ngx_rtmp_amf_put(ctx, buf, 4) != NGX_OK)
{
return NGX_ERROR;
}
@@ -554,9 +534,7 @@ ngx_rtmp_amf_write(ngx_rtmp_amf_ctx_t *ctx,
switch(type) {
case NGX_RTMP_AMF_NUMBER:
- if (ngx_rtmp_amf_put(ctx,
- ngx_rtmp_amf_reverse_copy(buf,
- data, 8), 8) != NGX_OK)
+ if (ngx_rtmp_amf_put(ctx, data, 8) != NGX_OK)
{
return NGX_ERROR;
}
@@ -573,9 +551,8 @@ ngx_rtmp_amf_write(ngx_rtmp_amf_ctx_t *ctx,
len = (uint16_t) ngx_strlen((u_char*) data);
}
- if (ngx_rtmp_amf_put(ctx,
- ngx_rtmp_amf_reverse_copy(buf,
- &len, 2), 2) != NGX_OK)
+ *(uint16_t*)buf = htons(len);
+ if (ngx_rtmp_amf_put(ctx, buf, 2) != NGX_OK)
{
return NGX_ERROR;
}
@@ -621,18 +598,16 @@ ngx_rtmp_amf_write(ngx_rtmp_amf_ctx_t *ctx,
break;
case NGX_RTMP_AMF_INT16:
- if (ngx_rtmp_amf_put(ctx,
- ngx_rtmp_amf_reverse_copy(buf,
- data, 2), 2) != NGX_OK)
+ *(uint16_t*)buf = htons(*(uint16_t*)data);
+ if (ngx_rtmp_amf_put(ctx, buf, 2) != NGX_OK)
{
return NGX_ERROR;
}
break;
case NGX_RTMP_AMF_INT32:
- if (ngx_rtmp_amf_put(ctx,
- ngx_rtmp_amf_reverse_copy(buf,
- data, 4), 4) != NGX_OK)
+ *(uint32_t*)buf = htonl(*(uint32_t*)data);
+ if (ngx_rtmp_amf_put(ctx, buf, 4) != NGX_OK)
{
return NGX_ERROR;
}
diff --git a/ngx_rtmp_bitop.h b/ngx_rtmp_bitop.h
index c954a35f3..25133d284 100644
--- a/ngx_rtmp_bitop.h
+++ b/ngx_rtmp_bitop.h
@@ -40,7 +40,7 @@ uint64_t ngx_rtmp_bit_read_golomb(ngx_rtmp_bit_reader_t *br);
((uint32_t) ngx_rtmp_bit_read(br, 32))
#define ngx_rtmp_bit_read_64(br) \
- ((uint64_t) ngx_rtmp_read(br, 64))
+ ((uint64_t) ngx_rtmp_bit_read(br, 64))
#endif /* _NGX_RTMP_BITOP_H_INCLUDED_ */
diff --git a/ngx_rtmp_eval.c b/ngx_rtmp_eval.c
index 1e5195a90..7ee7c7bac 100644
--- a/ngx_rtmp_eval.c
+++ b/ngx_rtmp_eval.c
@@ -166,7 +166,7 @@ ngx_rtmp_eval(void *ctx, ngx_str_t *in, ngx_rtmp_eval_t **e, ngx_str_t *out,
state = ESCAPE;
continue;
}
-
+ /* fall through */
case ESCAPE:
ngx_rtmp_eval_append(&b, &c, 1, log);
state = NORMAL;
diff --git a/ngx_rtmp_flv_module.c b/ngx_rtmp_flv_module.c
index 4776e5419..cd5162f7d 100644
--- a/ngx_rtmp_flv_module.c
+++ b/ngx_rtmp_flv_module.c
@@ -102,7 +102,7 @@ ngx_rtmp_flv_fill_index(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_flv_index_t *idx)
return NGX_ERROR;
}
- ngx_rtmp_rmemcpy(&nelts, b->pos + ctx->offset, 4);
+ nelts=htonl(*(uint32_t*)(b->pos + ctx->offset));
idx->nelts = nelts;
idx->offset = ctx->offset + 4;
@@ -201,11 +201,7 @@ ngx_rtmp_flv_init_index(ngx_rtmp_session_t *s, ngx_chain_t *in)
static double
ngx_rtmp_flv_index_value(void *src)
{
- double v;
-
- ngx_rtmp_rmemcpy(&v, src, 8);
-
- return v;
+ return *(double*)src;
}
@@ -352,8 +348,7 @@ ngx_rtmp_flv_read_meta(ngx_rtmp_session_t *s, ngx_file_t *f)
h.msid = NGX_RTMP_MSID;
h.csid = NGX_RTMP_CSID_AMF;
- size = 0;
- ngx_rtmp_rmemcpy(&size, ngx_rtmp_flv_header + 1, 3);
+ size = n3toh4(ngx_rtmp_flv_header + 1);
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"flv: metadata size=%D", size);
@@ -440,12 +435,9 @@ ngx_rtmp_flv_send(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_uint_t *ts)
h.msid = NGX_RTMP_MSID;
h.type = ngx_rtmp_flv_header[0];
- size = 0;
-
- ngx_rtmp_rmemcpy(&size, ngx_rtmp_flv_header + 1, 3);
- ngx_rtmp_rmemcpy(&h.timestamp, ngx_rtmp_flv_header + 4, 3);
-
- ((u_char *) &h.timestamp)[3] = ngx_rtmp_flv_header[7];
+ size = n3toh4(ngx_rtmp_flv_header + 1);
+ h.timestamp = n3toh4(ngx_rtmp_flv_header + 4);
+ h.timestamp |= ((uint32_t) ngx_rtmp_flv_header[7] << 24);
ctx->offset += (sizeof(ngx_rtmp_flv_header) + size + 4);
diff --git a/ngx_rtmp_handler.c b/ngx_rtmp_handler.c
index a8bef611a..5611ec88c 100644
--- a/ngx_rtmp_handler.c
+++ b/ngx_rtmp_handler.c
@@ -200,7 +200,7 @@ ngx_rtmp_recv(ngx_event_t *rev)
ngx_rtmp_stream_t *st, *st0;
ngx_chain_t *in, *head;
ngx_buf_t *b;
- u_char *p, *pp, *old_pos;
+ u_char *p, *old_pos;
size_t size, fsize, old_size;
uint8_t fmt, ext;
uint32_t csid, timestamp;
@@ -308,14 +308,14 @@ ngx_rtmp_recv(ngx_event_t *rev)
if (b->last - p < 1)
continue;
csid = 64;
- csid += *(uint8_t*)p++;
+ csid += *p++;
} else if (csid == 1) {
if (b->last - p < 2)
continue;
csid = 64;
- csid += *(uint8_t*)p++;
- csid += (uint32_t)256 * (*(uint8_t*)p++);
+ csid += *p++;
+ csid += ((uint32_t) *p++ << 8);
}
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, c->log, 0,
@@ -355,40 +355,37 @@ ngx_rtmp_recv(ngx_event_t *rev)
if (fmt <= 2 ) {
if (b->last - p < 3)
continue;
- /* timestamp:
- * big-endian 3b -> little-endian 4b */
- pp = (u_char*)×tamp;
- pp[2] = *p++;
- pp[1] = *p++;
- pp[0] = *p++;
- pp[3] = 0;
+
+ /* timestamp: big-endian 3 bytes */
+
+ timestamp = ((uint32_t) *p++ << 16);
+ timestamp |= ((uint32_t) *p++ << 8);
+ timestamp |= *p++;
ext = (timestamp == 0x00ffffff);
if (fmt <= 1) {
if (b->last - p < 4)
continue;
- /* size:
- * big-endian 3b -> little-endian 4b
- * type:
- * 1b -> 1b*/
- pp = (u_char*)&h->mlen;
- pp[2] = *p++;
- pp[1] = *p++;
- pp[0] = *p++;
- pp[3] = 0;
- h->type = *(uint8_t*)p++;
+
+ /* size: big-endian 3 bytes */
+
+ h->mlen = ((uint32_t) *p++ << 16);
+ h->mlen |= ((uint32_t) *p++ << 8);
+ h->mlen |= *p++;
+
+ h->type = *p++;
if (fmt == 0) {
if (b->last - p < 4)
continue;
- /* stream:
- * little-endian 4b -> little-endian 4b */
- pp = (u_char*)&h->msid;
- pp[0] = *p++;
- pp[1] = *p++;
- pp[2] = *p++;
- pp[3] = *p++;
+
+ /* stream: little-endian 4 bytes */
+
+ h->msid = *p++;
+ h->msid |= ((uint32_t) *p++ << 8);
+ h->msid |= ((uint32_t) *p++ << 16);
+ h->msid |= ((uint32_t) *p++ << 24);
}
}
}
@@ -397,25 +394,38 @@ ngx_rtmp_recv(ngx_event_t *rev)
if (ext) {
if (b->last - p < 4)
continue;
- pp = (u_char*)×tamp;
- pp[3] = *p++;
- pp[2] = *p++;
- pp[1] = *p++;
- pp[0] = *p++;
+
+ /* timestamp: big-endian 4 bytes */
+
+ timestamp = ((uint32_t) *p++ << 24);
+ timestamp |= ((uint32_t) *p++ << 16);
+ timestamp |= ((uint32_t) *p++ << 8);
+ timestamp |= *p++;
+ ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP extended timestamp %uD", (uint32_t)timestamp);
}
- if (st->len == 0) {
- /* Messages with type=3 should
- * never have ext timestamp field
- * according to standard.
- * However that's not always the case
- * in real life */
- st->ext = (ext && cscf->publish_time_fix);
+ /* type 0,1,2 */
+ if (fmt <= 2) {
+ /* The specification states that ext timestamp
+ * is present in Type 3 chunks when the most recent Type 0,
+ * 1, or 2 chunk for the same chunk stream ID have the presence of
+ * an extended timestamp field. */
+ st->ext = ext;
if (fmt) {
+ /* type 1,2 => timestamp delta */
+ ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP timestamp delta on fmt type %d", (int)fmt);
st->dtime = timestamp;
} else {
- h->timestamp = timestamp;
- st->dtime = 0;
+ /* type */
+ /* fix elemental live server sending garbage ts ?! */
+ if ((int)h->type == 18) {
+ ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP FIX fmt type %d type 18", (int)fmt);
+ st->dtime = 0;
+ } else {
+ h->timestamp = timestamp;
+ st->dtime = 0;
+ ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP absolute timestamp on fmt type 0 : %uD", h->timestamp);
+ }
}
}
@@ -430,7 +440,7 @@ ngx_rtmp_recv(ngx_event_t *rev)
if (h->mlen > cscf->max_message) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
- "too big message: %uz", cscf->max_message);
+ "too big message: %uz > %uz ", h->mlen, cscf->max_message);
ngx_rtmp_finalize_session(s);
return;
}
@@ -571,7 +581,7 @@ ngx_rtmp_prepare_message(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_rtmp_header_t *lh, ngx_chain_t *out)
{
ngx_chain_t *l;
- u_char *p, *pp;
+ u_char *p;
ngx_int_t hsize, thsize, nbufs;
uint32_t mlen, timestamp, ext_timestamp;
static uint8_t hdrsize[] = { 12, 8, 4, 1 };
@@ -664,33 +674,36 @@ ngx_rtmp_prepare_message(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
/* message header */
if (fmt <= 2) {
- pp = (u_char*)×tamp;
- *p++ = pp[2];
- *p++ = pp[1];
- *p++ = pp[0];
+
+ *p++ = (u_char) (timestamp >> 16);
+ *p++ = (u_char) (timestamp >> 8);
+ *p++ = (u_char) timestamp;
+
if (fmt <= 1) {
- pp = (u_char*)&mlen;
- *p++ = pp[2];
- *p++ = pp[1];
- *p++ = pp[0];
+
+ *p++ = (u_char) (mlen >> 16);
+ *p++ = (u_char) (mlen >> 8);
+ *p++ = (u_char) mlen;
+
*p++ = h->type;
+
if (fmt == 0) {
- pp = (u_char*)&h->msid;
- *p++ = pp[0];
- *p++ = pp[1];
- *p++ = pp[2];
- *p++ = pp[3];
+
+ *p++ = (u_char) h->msid;
+ *p++ = (u_char) (h->msid >> 8);
+ *p++ = (u_char) (h->msid >> 16);
+ *p++ = (u_char) (h->msid >> 24);
}
}
}
/* extended header */
if (ext_timestamp) {
- pp = (u_char*)&ext_timestamp;
- *p++ = pp[3];
- *p++ = pp[2];
- *p++ = pp[1];
- *p++ = pp[0];
+
+ *p++ = (u_char) (ext_timestamp >> 24);
+ *p++ = (u_char) (ext_timestamp >> 16);
+ *p++ = (u_char) (ext_timestamp >> 8);
+ *p++ = (u_char) ext_timestamp;
/* This CONTRADICTS the standard
* but that's the way flash client
@@ -774,8 +787,8 @@ ngx_rtmp_receive_message(ngx_rtmp_session_t *s,
ch = ch->next, ++nbufs);
ngx_log_debug7(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
- "RTMP recv %s (%d) csid=%D timestamp=%D "
- "mlen=%D msid=%D nbufs=%d",
+ "RTMP recv %s (%d) csid=%D timestamp=%uD "
+ "mlen=%uD msid=%D nbufs=%d",
ngx_rtmp_message_type(h->type), (int)h->type,
h->csid, h->timestamp, h->mlen, h->msid, nbufs);
}
diff --git a/ngx_rtmp_handshake.c b/ngx_rtmp_handshake.c
index 409d9a0dd..8590cdcb2 100644
--- a/ngx_rtmp_handshake.c
+++ b/ngx_rtmp_handshake.c
@@ -264,7 +264,8 @@ ngx_rtmp_handshake_create_challenge(ngx_rtmp_session_t *s,
b = s->hs_buf;
b->last = b->pos = b->start;
*b->last++ = '\x03';
- b->last = ngx_rtmp_rcpymem(b->last, &s->epoch, 4);
+ *(uint32_t*)b->last=htonl(s->epoch);
+ b->last +=4;
b->last = ngx_cpymem(b->last, version, 4);
ngx_rtmp_fill_random_buffer(b);
++b->pos;
@@ -292,8 +293,7 @@ ngx_rtmp_handshake_parse_challenge(ngx_rtmp_session_t *s,
return NGX_ERROR;
}
++b->pos;
- s->peer_epoch = 0;
- ngx_rtmp_rmemcpy(&s->peer_epoch, b->pos, 4);
+ s->peer_epoch = ntohl(*(uint32_t*)b->pos);
p = b->pos + 4;
ngx_log_debug5(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
diff --git a/ngx_rtmp_live_module.c b/ngx_rtmp_live_module.c
index 8380d94c5..f4295f866 100644
--- a/ngx_rtmp_live_module.c
+++ b/ngx_rtmp_live_module.c
@@ -1253,7 +1253,7 @@ ngx_rtmp_live_on_fi(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
if (res == NGX_OK) {
- ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
+ ngx_log_debug2(NGX_LOG_DEBUG, s->connection->log, 0,
"live: onFi: date='%s', time='%s'",
v.date, v.time);
diff --git a/ngx_rtmp_mp4_module.c b/ngx_rtmp_mp4_module.c
index 0259ca2f6..e39bb1bdb 100644
--- a/ngx_rtmp_mp4_module.c
+++ b/ngx_rtmp_mp4_module.c
@@ -528,9 +528,9 @@ ngx_rtmp_mp4_parse_mdhd(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
}
pos += 12;
- t->time_scale = ngx_rtmp_r32(*(uint32_t *) pos);
+ t->time_scale = ntohl(*(uint32_t *) pos);
pos += 4;
- t->duration = ngx_rtmp_r32(*(uint32_t *) pos);
+ t->duration = ntohl(*(uint32_t *) pos);
break;
case 1:
@@ -539,9 +539,9 @@ ngx_rtmp_mp4_parse_mdhd(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
}
pos += 20;
- t->time_scale = ngx_rtmp_r32(*(uint32_t *) pos);
+ t->time_scale = ntohl(*(uint32_t *) pos);
pos += 4;
- t->duration = ngx_rtmp_r64(*(uint64_t *) pos);
+ t->duration = ntohll(*(uint64_t *) pos);
break;
default:
@@ -616,11 +616,11 @@ ngx_rtmp_mp4_parse_video(ngx_rtmp_session_t *s, u_char *pos, u_char *last,
pos += 24;
- ctx->width = ngx_rtmp_r16(*(uint16_t *) pos);
+ ctx->width = ntohs(*(uint16_t *) pos);
pos += 2;
- ctx->height = ngx_rtmp_r16(*(uint16_t *) pos);
+ ctx->height = ntohs(*(uint16_t *) pos);
pos += 52;
@@ -660,19 +660,19 @@ ngx_rtmp_mp4_parse_audio(ngx_rtmp_session_t *s, u_char *pos, u_char *last,
pos += 8;
- version = ngx_rtmp_r16(*(uint16_t *) pos);
+ version = ntohs(*(uint16_t *) pos);
pos += 8;
- ctx->nchannels = ngx_rtmp_r16(*(uint16_t *) pos);
+ ctx->nchannels = ntohs(*(uint16_t *) pos);
pos += 2;
- ctx->sample_size = ngx_rtmp_r16(*(uint16_t *) pos);
+ ctx->sample_size = ntohs(*(uint16_t *) pos);
pos += 6;
- ctx->sample_rate = ngx_rtmp_r16(*(uint16_t *) pos);
+ ctx->sample_rate = ntohs(*(uint16_t *) pos);
pos += 4;
@@ -862,7 +862,7 @@ ngx_rtmp_mp4_parse_es(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
return NGX_ERROR;
}
- id = ngx_rtmp_r16(*(uint16_t *) pos);
+ id = ntohs(*(uint16_t *) pos);
pos += 2;
flags = *(uint8_t *) pos;
@@ -1018,13 +1018,13 @@ ngx_rtmp_mp4_parse_stsc(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
t->chunks = (ngx_rtmp_mp4_chunks_t *) pos;
- if (pos + sizeof(*t->chunks) + ngx_rtmp_r32(t->chunks->entry_count) *
+ if (pos + sizeof(*t->chunks) + ntohl(t->chunks->entry_count) *
sizeof(t->chunks->entries[0])
<= last)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: chunks entries=%uD",
- ngx_rtmp_r32(t->chunks->entry_count));
+ ntohl(t->chunks->entry_count));
return NGX_OK;
}
@@ -1049,13 +1049,13 @@ ngx_rtmp_mp4_parse_stts(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
t->times = (ngx_rtmp_mp4_times_t *) pos;
- if (pos + sizeof(*t->times) + ngx_rtmp_r32(t->times->entry_count) *
+ if (pos + sizeof(*t->times) + ntohl(t->times->entry_count) *
sizeof(t->times->entries[0])
<= last)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: times entries=%uD",
- ngx_rtmp_r32(t->times->entry_count));
+ ntohl(t->times->entry_count));
return NGX_OK;
}
@@ -1080,13 +1080,13 @@ ngx_rtmp_mp4_parse_ctts(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
t->delays = (ngx_rtmp_mp4_delays_t *) pos;
- if (pos + sizeof(*t->delays) + ngx_rtmp_r32(t->delays->entry_count) *
+ if (pos + sizeof(*t->delays) + ntohl(t->delays->entry_count) *
sizeof(t->delays->entries[0])
<= last)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: delays entries=%uD",
- ngx_rtmp_r32(t->delays->entry_count));
+ ntohl(t->delays->entry_count));
return NGX_OK;
}
@@ -1111,13 +1111,13 @@ ngx_rtmp_mp4_parse_stss(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
t->keys = (ngx_rtmp_mp4_keys_t *) pos;
- if (pos + sizeof(*t->keys) + ngx_rtmp_r32(t->keys->entry_count) *
+ if (pos + sizeof(*t->keys) + ntohl(t->keys->entry_count) *
sizeof(t->keys->entries[0])
<= last)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: keys entries=%uD",
- ngx_rtmp_r32(t->keys->entry_count));
+ ntohl(t->keys->entry_count));
return NGX_OK;
}
@@ -1145,18 +1145,18 @@ ngx_rtmp_mp4_parse_stsz(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
if (pos + sizeof(*t->sizes) <= last && t->sizes->sample_size) {
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: sizes size=%uD",
- ngx_rtmp_r32(t->sizes->sample_size));
+ ntohl(t->sizes->sample_size));
return NGX_OK;
}
- if (pos + sizeof(*t->sizes) + ngx_rtmp_r32(t->sizes->sample_count) *
+ if (pos + sizeof(*t->sizes) + ntohl(t->sizes->sample_count) *
sizeof(t->sizes->entries[0])
<= last)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: sizes entries=%uD",
- ngx_rtmp_r32(t->sizes->sample_count));
+ ntohl(t->sizes->sample_count));
return NGX_OK;
}
@@ -1181,14 +1181,14 @@ ngx_rtmp_mp4_parse_stz2(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
t->sizes2 = (ngx_rtmp_mp4_sizes2_t *) pos;
- if (pos + sizeof(*t->sizes) + ngx_rtmp_r32(t->sizes2->sample_count) *
- ngx_rtmp_r32(t->sizes2->field_size) / 8
+ if (pos + sizeof(*t->sizes) + ntohl(t->sizes2->sample_count) *
+ ntohl(t->sizes2->field_size) / 8
<= last)
{
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: sizes2 field_size=%uD entries=%uD",
- ngx_rtmp_r32(t->sizes2->field_size),
- ngx_rtmp_r32(t->sizes2->sample_count));
+ ntohl(t->sizes2->field_size),
+ ntohl(t->sizes2->sample_count));
return NGX_OK;
}
@@ -1213,13 +1213,13 @@ ngx_rtmp_mp4_parse_stco(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
t->offsets = (ngx_rtmp_mp4_offsets_t *) pos;
- if (pos + sizeof(*t->offsets) + ngx_rtmp_r32(t->offsets->entry_count) *
+ if (pos + sizeof(*t->offsets) + ntohl(t->offsets->entry_count) *
sizeof(t->offsets->entries[0])
<= last)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: offsets entries=%uD",
- ngx_rtmp_r32(t->offsets->entry_count));
+ ntohl(t->offsets->entry_count));
return NGX_OK;
}
@@ -1244,13 +1244,13 @@ ngx_rtmp_mp4_parse_co64(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
t->offsets64 = (ngx_rtmp_mp4_offsets64_t *) pos;
- if (pos + sizeof(*t->offsets64) + ngx_rtmp_r32(t->offsets64->entry_count) *
+ if (pos + sizeof(*t->offsets64) + ntohl(t->offsets64->entry_count) *
sizeof(t->offsets64->entries[0])
<= last)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: offsets64 entries=%uD",
- ngx_rtmp_r32(t->offsets64->entry_count));
+ ntohl(t->offsets64->entry_count));
return NGX_OK;
}
@@ -1275,7 +1275,7 @@ ngx_rtmp_mp4_parse(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
}
hdr = (uint32_t *) pos;
- size = ngx_rtmp_r32(hdr[0]);
+ size = ntohl(hdr[0]);
tag = hdr[1];
if (pos + size > last) {
@@ -1318,11 +1318,11 @@ ngx_rtmp_mp4_next_time(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
cr = &t->cursor;
- if (cr->time_pos >= ngx_rtmp_r32(t->times->entry_count)) {
+ if (cr->time_pos >= ntohl(t->times->entry_count)) {
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui time[%ui/%uD] overflow",
t->id, cr->time_pos,
- ngx_rtmp_r32(t->times->entry_count));
+ ntohl(t->times->entry_count));
return NGX_ERROR;
}
@@ -1330,22 +1330,22 @@ ngx_rtmp_mp4_next_time(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
te = &t->times->entries[cr->time_pos];
cr->last_timestamp = cr->timestamp;
- cr->timestamp += ngx_rtmp_r32(te->sample_delta);
+ cr->timestamp += ntohl(te->sample_delta);
cr->not_first = 1;
ngx_log_debug8(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui time[%ui] [%ui/%uD][%ui/%uD]=%uD t=%uD",
t->id, cr->pos, cr->time_pos,
- ngx_rtmp_r32(t->times->entry_count),
- cr->time_count, ngx_rtmp_r32(te->sample_count),
- ngx_rtmp_r32(te->sample_delta),
+ ntohl(t->times->entry_count),
+ cr->time_count, ntohl(te->sample_count),
+ ntohl(te->sample_delta),
cr->timestamp);
cr->time_count++;
cr->pos++;
- if (cr->time_count >= ngx_rtmp_r32(te->sample_count)) {
+ if (cr->time_count >= ntohl(te->sample_count)) {
cr->time_pos++;
cr->time_count = 0;
}
@@ -1370,8 +1370,8 @@ ngx_rtmp_mp4_seek_time(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t,
te = t->times->entries;
- while (cr->time_pos < ngx_rtmp_r32(t->times->entry_count)) {
- dt = ngx_rtmp_r32(te->sample_delta) * ngx_rtmp_r32(te->sample_count);
+ while (cr->time_pos < ntohl(t->times->entry_count)) {
+ dt = ntohl(te->sample_delta) * ntohl(te->sample_count);
if (cr->timestamp + dt >= timestamp) {
if (te->sample_delta == 0) {
@@ -1379,24 +1379,24 @@ ngx_rtmp_mp4_seek_time(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t,
}
cr->time_count = (timestamp - cr->timestamp) /
- ngx_rtmp_r32(te->sample_delta);
- cr->timestamp += ngx_rtmp_r32(te->sample_delta) * cr->time_count;
+ ntohl(te->sample_delta);
+ cr->timestamp += ntohl(te->sample_delta) * cr->time_count;
cr->pos += cr->time_count;
break;
}
cr->timestamp += dt;
- cr->pos += ngx_rtmp_r32(te->sample_count);
+ cr->pos += ntohl(te->sample_count);
cr->time_pos++;
te++;
}
- if (cr->time_pos >= ngx_rtmp_r32(t->times->entry_count)) {
+ if (cr->time_pos >= ntohl(t->times->entry_count)) {
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui seek time[%ui/%uD] overflow",
t->id, cr->time_pos,
- ngx_rtmp_r32(t->times->entry_count));
+ ntohl(t->times->entry_count));
return NGX_ERROR;
}
@@ -1405,10 +1405,10 @@ ngx_rtmp_mp4_seek_time(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t,
"mp4: track#%ui seek time[%ui] [%ui/%uD][%ui/%uD]=%uD "
"t=%uD",
t->id, cr->pos, cr->time_pos,
- ngx_rtmp_r32(t->times->entry_count),
+ ntohl(t->times->entry_count),
cr->time_count,
- ngx_rtmp_r32(te->sample_count),
- ngx_rtmp_r32(te->sample_delta),
+ ntohl(te->sample_count),
+ ntohl(te->sample_delta),
cr->timestamp);
return NGX_OK;
@@ -1433,44 +1433,44 @@ ngx_rtmp_mp4_update_offset(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
chunk = cr->chunk - 1;
if (t->offsets) {
- if (chunk >= ngx_rtmp_r32(t->offsets->entry_count)) {
+ if (chunk >= ntohl(t->offsets->entry_count)) {
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui offset[%ui/%uD] overflow",
t->id, cr->chunk,
- ngx_rtmp_r32(t->offsets->entry_count));
+ ntohl(t->offsets->entry_count));
return NGX_ERROR;
}
- cr->offset = (off_t) ngx_rtmp_r32(t->offsets->entries[chunk]);
+ cr->offset = (off_t) ntohl(t->offsets->entries[chunk]);
cr->size = 0;
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui offset[%ui/%uD]=%O",
t->id, cr->chunk,
- ngx_rtmp_r32(t->offsets->entry_count),
+ ntohl(t->offsets->entry_count),
cr->offset);
return NGX_OK;
}
if (t->offsets64) {
- if (chunk >= ngx_rtmp_r32(t->offsets64->entry_count)) {
+ if (chunk >= ntohl(t->offsets64->entry_count)) {
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui offset64[%ui/%uD] overflow",
t->id, cr->chunk,
- ngx_rtmp_r32(t->offsets->entry_count));
+ ntohl(t->offsets->entry_count));
return NGX_ERROR;
}
- cr->offset = (off_t) ngx_rtmp_r64(t->offsets64->entries[chunk]);
+ cr->offset = (off_t) ntohll(t->offsets64->entries[chunk]);
cr->size = 0;
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui offset64[%ui/%uD]=%O",
t->id, cr->chunk,
- ngx_rtmp_r32(t->offsets->entry_count),
+ ntohl(t->offsets->entry_count),
cr->offset);
return NGX_OK;
@@ -1493,11 +1493,11 @@ ngx_rtmp_mp4_next_chunk(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
cr = &t->cursor;
- if (cr->chunk_pos >= ngx_rtmp_r32(t->chunks->entry_count)) {
+ if (cr->chunk_pos >= ntohl(t->chunks->entry_count)) {
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui chunk[%ui/%uD] overflow",
t->id, cr->chunk_pos,
- ngx_rtmp_r32(t->chunks->entry_count));
+ ntohl(t->chunks->entry_count));
return NGX_ERROR;
}
@@ -1506,13 +1506,13 @@ ngx_rtmp_mp4_next_chunk(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
cr->chunk_count++;
- if (cr->chunk_count >= ngx_rtmp_r32(ce->samples_per_chunk)) {
+ if (cr->chunk_count >= ntohl(ce->samples_per_chunk)) {
cr->chunk_count = 0;
cr->chunk++;
- if (cr->chunk_pos + 1 < ngx_rtmp_r32(t->chunks->entry_count)) {
+ if (cr->chunk_pos + 1 < ntohl(t->chunks->entry_count)) {
nce = ce + 1;
- if (cr->chunk >= ngx_rtmp_r32(nce->first_chunk)) {
+ if (cr->chunk >= ntohl(nce->first_chunk)) {
cr->chunk_pos++;
ce = nce;
}
@@ -1527,10 +1527,10 @@ ngx_rtmp_mp4_next_chunk(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
ngx_log_debug7(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui chunk[%ui/%uD][%uD..%ui][%ui/%uD]",
t->id, cr->chunk_pos,
- ngx_rtmp_r32(t->chunks->entry_count),
- ngx_rtmp_r32(ce->first_chunk),
+ ntohl(t->chunks->entry_count),
+ ntohl(ce->first_chunk),
cr->chunk, cr->chunk_count,
- ngx_rtmp_r32(ce->samples_per_chunk));
+ ntohl(ce->samples_per_chunk));
if (new_chunk) {
@@ -1558,12 +1558,12 @@ ngx_rtmp_mp4_seek_chunk(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
ce = t->chunks->entries;
pos = 0;
- while (cr->chunk_pos + 1 < ngx_rtmp_r32(t->chunks->entry_count)) {
+ while (cr->chunk_pos + 1 < ntohl(t->chunks->entry_count)) {
nce = ce + 1;
- dpos = (ngx_rtmp_r32(nce->first_chunk) -
- ngx_rtmp_r32(ce->first_chunk)) *
- ngx_rtmp_r32(ce->samples_per_chunk);
+ dpos = (ntohl(nce->first_chunk) -
+ ntohl(ce->first_chunk)) *
+ ntohl(ce->samples_per_chunk);
if (pos + dpos > cr->pos) {
break;
@@ -1578,20 +1578,20 @@ ngx_rtmp_mp4_seek_chunk(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
return NGX_ERROR;
}
- dchunk = (cr->pos - pos) / ngx_rtmp_r32(ce->samples_per_chunk);
+ dchunk = (cr->pos - pos) / ntohl(ce->samples_per_chunk);
- cr->chunk = ngx_rtmp_r32(ce->first_chunk) + dchunk;
+ cr->chunk = ntohl(ce->first_chunk) + dchunk;
cr->chunk_pos = (ngx_uint_t) (ce - t->chunks->entries);
cr->chunk_count = (ngx_uint_t) (cr->pos - pos - dchunk *
- ngx_rtmp_r32(ce->samples_per_chunk));
+ ntohl(ce->samples_per_chunk));
ngx_log_debug7(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui seek chunk[%ui/%uD][%uD..%ui][%ui/%uD]",
t->id, cr->chunk_pos,
- ngx_rtmp_r32(t->chunks->entry_count),
- ngx_rtmp_r32(ce->first_chunk),
+ ntohl(t->chunks->entry_count),
+ ntohl(ce->first_chunk),
cr->chunk, cr->chunk_count,
- ngx_rtmp_r32(ce->samples_per_chunk));
+ ntohl(ce->samples_per_chunk));
return ngx_rtmp_mp4_update_offset(s, t);
}
@@ -1608,7 +1608,7 @@ ngx_rtmp_mp4_next_size(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
if (t->sizes) {
if (t->sizes->sample_size) {
- cr->size = ngx_rtmp_r32(t->sizes->sample_size);
+ cr->size = ntohl(t->sizes->sample_size);
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui size fix=%uz",
@@ -1619,32 +1619,32 @@ ngx_rtmp_mp4_next_size(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
cr->size_pos++;
- if (cr->size_pos >= ngx_rtmp_r32(t->sizes->sample_count)) {
+ if (cr->size_pos >= ntohl(t->sizes->sample_count)) {
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui size[%ui/%uD] overflow",
t->id, cr->size_pos,
- ngx_rtmp_r32(t->sizes->sample_count));
+ ntohl(t->sizes->sample_count));
return NGX_ERROR;
}
- cr->size = ngx_rtmp_r32(t->sizes->entries[cr->size_pos]);
+ cr->size = ntohl(t->sizes->entries[cr->size_pos]);
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui size[%ui/%uD]=%uz",
t->id, cr->size_pos,
- ngx_rtmp_r32(t->sizes->sample_count),
+ ntohl(t->sizes->sample_count),
cr->size);
return NGX_OK;
}
if (t->sizes2) {
- if (cr->size_pos >= ngx_rtmp_r32(t->sizes2->sample_count)) {
+ if (cr->size_pos >= ntohl(t->sizes2->sample_count)) {
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui size[%ui/%uD] overflow",
t->id, cr->size_pos,
- ngx_rtmp_r32(t->sizes2->sample_count));
+ ntohl(t->sizes2->sample_count));
return NGX_ERROR;
}
@@ -1672,7 +1672,7 @@ ngx_rtmp_mp4_seek_size(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
if (t->sizes) {
if (t->sizes->sample_size) {
- cr->size = ngx_rtmp_r32(t->sizes->sample_size);
+ cr->size = ntohl(t->sizes->sample_size);
cr->offset += cr->size * cr->chunk_count;
@@ -1683,37 +1683,37 @@ ngx_rtmp_mp4_seek_size(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
return NGX_OK;
}
- if (cr->pos >= ngx_rtmp_r32(t->sizes->sample_count)) {
+ if (cr->pos >= ntohl(t->sizes->sample_count)) {
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui seek size[%ui/%uD] overflow",
t->id, cr->pos,
- ngx_rtmp_r32(t->sizes->sample_count));
+ ntohl(t->sizes->sample_count));
return NGX_ERROR;
}
for (pos = 1; pos <= cr->chunk_count; ++pos) {
- cr->offset += ngx_rtmp_r32(t->sizes->entries[cr->pos - pos]);
+ cr->offset += ntohl(t->sizes->entries[cr->pos - pos]);
}
cr->size_pos = cr->pos;
- cr->size = ngx_rtmp_r32(t->sizes->entries[cr->size_pos]);
+ cr->size = ntohl(t->sizes->entries[cr->size_pos]);
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui seek size[%ui/%uD]=%uz",
t->id, cr->size_pos,
- ngx_rtmp_r32(t->sizes->sample_count),
+ ntohl(t->sizes->sample_count),
cr->size);
return NGX_OK;
}
if (t->sizes2) {
- if (cr->size_pos >= ngx_rtmp_r32(t->sizes2->sample_count)) {
+ if (cr->size_pos >= ntohl(t->sizes2->sample_count)) {
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui seek size2[%ui/%uD] overflow",
t->id, cr->size_pos,
- ngx_rtmp_r32(t->sizes->sample_count));
+ ntohl(t->sizes->sample_count));
return NGX_ERROR;
}
@@ -1744,11 +1744,11 @@ ngx_rtmp_mp4_next_key(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
cr->key_pos++;
}
- if (cr->key_pos >= ngx_rtmp_r32(t->keys->entry_count)) {
+ if (cr->key_pos >= ntohl(t->keys->entry_count)) {
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui key[%ui/%uD] overflow",
t->id, cr->key_pos,
- ngx_rtmp_r32(t->keys->entry_count));
+ ntohl(t->keys->entry_count));
cr->key = 0;
@@ -1756,13 +1756,13 @@ ngx_rtmp_mp4_next_key(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
}
ke = &t->keys->entries[cr->key_pos];
- cr->key = (cr->pos + 1 == ngx_rtmp_r32(*ke));
+ cr->key = (cr->pos + 1 == ntohl(*ke));
ngx_log_debug6(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui key[%ui/%uD][%ui/%uD]=%s",
t->id, cr->key_pos,
- ngx_rtmp_r32(t->keys->entry_count),
- cr->pos, ngx_rtmp_r32(*ke),
+ ntohl(t->keys->entry_count),
+ cr->pos, ntohl(*ke),
cr->key ? "match" : "miss");
return NGX_OK;
@@ -1782,27 +1782,27 @@ ngx_rtmp_mp4_seek_key(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
return NGX_OK;
}
- while (cr->key_pos < ngx_rtmp_r32(t->keys->entry_count)) {
- if (ngx_rtmp_r32(t->keys->entries[cr->key_pos]) > cr->pos) {
+ while (cr->key_pos < ntohl(t->keys->entry_count)) {
+ if (ntohl(t->keys->entries[cr->key_pos]) > cr->pos) {
break;
}
cr->key_pos++;
}
- if (cr->key_pos >= ngx_rtmp_r32(t->keys->entry_count)) {
+ if (cr->key_pos >= ntohl(t->keys->entry_count)) {
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui seek key[%ui/%uD] overflow",
t->id, cr->key_pos,
- ngx_rtmp_r32(t->keys->entry_count));
+ ntohl(t->keys->entry_count));
return NGX_OK;
}
ke = &t->keys->entries[cr->key_pos];
- /*cr->key = (cr->pos + 1 == ngx_rtmp_r32(*ke));*/
+ /*cr->key = (cr->pos + 1 == ntohl(*ke));*/
/* distance to the next keyframe */
- dpos = ngx_rtmp_r32(*ke) - cr->pos - 1;
+ dpos = ntohl(*ke) - cr->pos - 1;
cr->key = 1;
/* TODO: range version needed */
@@ -1810,13 +1810,13 @@ ngx_rtmp_mp4_seek_key(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
ngx_rtmp_mp4_next_time(s, t);
}
-/* cr->key = (cr->pos + 1 == ngx_rtmp_r32(*ke));*/
+/* cr->key = (cr->pos + 1 == ntohl(*ke));*/
ngx_log_debug6(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui seek key[%ui/%uD][%ui/%uD]=%s",
t->id, cr->key_pos,
- ngx_rtmp_r32(t->keys->entry_count),
- cr->pos, ngx_rtmp_r32(*ke),
+ ntohl(t->keys->entry_count),
+ cr->pos, ntohl(*ke),
cr->key ? "match" : "miss");
return NGX_OK;
@@ -1835,11 +1835,11 @@ ngx_rtmp_mp4_next_delay(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
return NGX_OK;
}
- if (cr->delay_pos >= ngx_rtmp_r32(t->delays->entry_count)) {
+ if (cr->delay_pos >= ntohl(t->delays->entry_count)) {
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui delay[%ui/%uD] overflow",
t->id, cr->delay_pos,
- ngx_rtmp_r32(t->delays->entry_count));
+ ntohl(t->delays->entry_count));
return NGX_OK;
}
@@ -1847,29 +1847,29 @@ ngx_rtmp_mp4_next_delay(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
cr->delay_count++;
de = &t->delays->entries[cr->delay_pos];
- if (cr->delay_count >= ngx_rtmp_r32(de->sample_count)) {
+ if (cr->delay_count >= ntohl(de->sample_count)) {
cr->delay_pos++;
de++;
cr->delay_count = 0;
}
- if (cr->delay_pos >= ngx_rtmp_r32(t->delays->entry_count)) {
+ if (cr->delay_pos >= ntohl(t->delays->entry_count)) {
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui delay[%ui/%uD] overflow",
t->id, cr->delay_pos,
- ngx_rtmp_r32(t->delays->entry_count));
+ ntohl(t->delays->entry_count));
return NGX_OK;
}
- cr->delay = ngx_rtmp_r32(de->sample_offset);
+ cr->delay = ntohl(de->sample_offset);
ngx_log_debug6(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui delay[%ui/%uD][%ui/%uD]=%ui",
t->id, cr->delay_pos,
- ngx_rtmp_r32(t->delays->entry_count),
+ ntohl(t->delays->entry_count),
cr->delay_count,
- ngx_rtmp_r32(de->sample_count), cr->delay);
+ ntohl(de->sample_count), cr->delay);
return NGX_OK;
}
@@ -1891,12 +1891,12 @@ ngx_rtmp_mp4_seek_delay(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
pos = 0;
de = t->delays->entries;
- while (cr->delay_pos < ngx_rtmp_r32(t->delays->entry_count)) {
- dpos = ngx_rtmp_r32(de->sample_count);
+ while (cr->delay_pos < ntohl(t->delays->entry_count)) {
+ dpos = ntohl(de->sample_count);
if (pos + dpos > cr->pos) {
cr->delay_count = cr->pos - pos;
- cr->delay = ngx_rtmp_r32(de->sample_offset);
+ cr->delay = ntohl(de->sample_offset);
break;
}
@@ -1905,11 +1905,11 @@ ngx_rtmp_mp4_seek_delay(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
de++;
}
- if (cr->delay_pos >= ngx_rtmp_r32(t->delays->entry_count)) {
+ if (cr->delay_pos >= ntohl(t->delays->entry_count)) {
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui seek delay[%ui/%uD] overflow",
t->id, cr->delay_pos,
- ngx_rtmp_r32(t->delays->entry_count));
+ ntohl(t->delays->entry_count));
return NGX_OK;
}
@@ -1917,9 +1917,9 @@ ngx_rtmp_mp4_seek_delay(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
ngx_log_debug6(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui seek delay[%ui/%uD][%ui/%uD]=%ui",
t->id, cr->delay_pos,
- ngx_rtmp_r32(t->delays->entry_count),
+ ntohl(t->delays->entry_count),
cr->delay_count,
- ngx_rtmp_r32(de->sample_count), cr->delay);
+ ntohl(de->sample_count), cr->delay);
return NGX_OK;
}
@@ -2348,7 +2348,7 @@ ngx_rtmp_mp4_init(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_int_t aindex,
return NGX_ERROR;
}
- size = (size_t) ngx_rtmp_r32(hdr[0]);
+ size = (size_t) ntohl(hdr[0]);
shift = sizeof(hdr);
if (size == 1) {
@@ -2362,7 +2362,7 @@ ngx_rtmp_mp4_init(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_int_t aindex,
return NGX_ERROR;
}
- size = (size_t) ngx_rtmp_r64(extended_size);
+ size = (size_t) ntohll(extended_size);
shift += sizeof(extended_size);
} else if (size == 0) {
diff --git a/ngx_rtmp_receive.c b/ngx_rtmp_receive.c
index 73d617cf2..9c8705691 100644
--- a/ngx_rtmp_receive.c
+++ b/ngx_rtmp_receive.c
@@ -17,7 +17,6 @@ ngx_rtmp_protocol_message_handler(ngx_rtmp_session_t *s,
ngx_rtmp_header_t *h, ngx_chain_t *in)
{
ngx_buf_t *b;
- u_char *p;
uint32_t val;
uint8_t limit;
@@ -30,11 +29,7 @@ ngx_rtmp_protocol_message_handler(ngx_rtmp_session_t *s,
return NGX_OK;
}
- p = (u_char*)&val;
- p[0] = b->pos[3];
- p[1] = b->pos[2];
- p[2] = b->pos[1];
- p[3] = b->pos[0];
+ val=ntohl(*(uint32_t*)&b->pos[0]);
switch(h->type) {
case NGX_RTMP_MSG_CHUNK_SIZE:
@@ -88,7 +83,6 @@ ngx_rtmp_user_message_handler(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
ngx_buf_t *b;
- u_char *p;
uint16_t evt;
uint32_t val;
@@ -101,21 +95,13 @@ ngx_rtmp_user_message_handler(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
return NGX_OK;
}
- p = (u_char*)&evt;
-
- p[0] = b->pos[1];
- p[1] = b->pos[0];
+ evt=ntohs(*(uint16_t*)&b->pos[0]);
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"RTMP recv user evt %s (%i)",
ngx_rtmp_user_message_type(evt), (ngx_int_t) evt);
- p = (u_char *) &val;
-
- p[0] = b->pos[5];
- p[1] = b->pos[4];
- p[2] = b->pos[3];
- p[3] = b->pos[2];
+ val=ntohl(*(uint32_t*)&b->pos[2]);
switch(evt) {
case NGX_RTMP_USER_STREAM_BEGIN:
@@ -164,12 +150,7 @@ ngx_rtmp_user_message_handler(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
return NGX_OK;
}
- p = (u_char *) &v.buflen;
-
- p[0] = b->pos[9];
- p[1] = b->pos[8];
- p[2] = b->pos[7];
- p[3] = b->pos[6];
+ v.buflen=ntohl(*(uint32_t*)&b->pos[6]);
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"receive: set_buflen msid=%uD buflen=%uD",
@@ -240,18 +221,20 @@ ngx_rtmp_fetch_uint8(ngx_chain_t **in, uint8_t *ret)
static ngx_int_t
ngx_rtmp_fetch_uint32(ngx_chain_t **in, uint32_t *ret, ngx_int_t n)
{
- u_char *r = (u_char *) ret;
+ u_char b;
+ uint32_t val=0;
ngx_int_t rc;
- *ret = 0;
-
while (--n >= 0) {
- rc = ngx_rtmp_fetch(in, &r[n]);
+ rc = ngx_rtmp_fetch(in, &b);
if (rc != NGX_OK) {
+ *ret = 0;
return rc;
}
+ val = (val<<8)|b;
}
+ *ret=val;
return NGX_OK;
}
diff --git a/ngx_rtmp_record_module.c b/ngx_rtmp_record_module.c
index c7db8edfa..abba56e15 100644
--- a/ngx_rtmp_record_module.c
+++ b/ngx_rtmp_record_module.c
@@ -454,7 +454,7 @@ ngx_rtmp_record_node_open(ngx_rtmp_session_t *s,
ngx_err_t err;
ngx_str_t path;
ngx_int_t mode, create_mode;
- u_char buf[8], *p;
+ u_char buf[8];
off_t file_size;
uint32_t tag_size, mlen, timestamp;
@@ -551,11 +551,7 @@ ngx_rtmp_record_node_open(ngx_rtmp_session_t *s,
goto done;
}
- p = (u_char *) &tag_size;
- p[0] = buf[3];
- p[1] = buf[2];
- p[2] = buf[1];
- p[3] = buf[0];
+ tag_size=ntohl(*(uint32_t*)&buf[0]);
if (tag_size == 0 || tag_size + 4 > file_size) {
file_size = 0;
@@ -569,11 +565,7 @@ ngx_rtmp_record_node_open(ngx_rtmp_session_t *s,
goto done;
}
- p = (u_char *) &mlen;
- p[0] = buf[3];
- p[1] = buf[2];
- p[2] = buf[1];
- p[3] = 0;
+ mlen=n3toh4(&buf[1]);
if (tag_size != mlen + 11) {
ngx_log_error(NGX_LOG_CRIT, s->connection->log, ngx_errno,
@@ -582,11 +574,7 @@ ngx_rtmp_record_node_open(ngx_rtmp_session_t *s,
goto done;
}
- p = (u_char *) ×tamp;
- p[3] = buf[7];
- p[0] = buf[6];
- p[1] = buf[5];
- p[2] = buf[4];
+ timestamp=n3toh4(&buf[4])|((uint32_t)buf[7]<<24);
done:
rctx->file.offset = file_size;
@@ -891,7 +879,7 @@ ngx_rtmp_record_write_frame(ngx_rtmp_session_t *s,
ngx_rtmp_header_t *h, ngx_chain_t *in,
ngx_int_t inc_nframes)
{
- u_char hdr[11], *p, *ph;
+ u_char hdr[11], *ph;
uint32_t timestamp, tag_size;
ngx_rtmp_record_app_conf_t *rracf;
@@ -937,16 +925,10 @@ ngx_rtmp_record_write_frame(ngx_rtmp_session_t *s,
*ph++ = (u_char)h->type;
- p = (u_char*)&h->mlen;
- *ph++ = p[2];
- *ph++ = p[1];
- *ph++ = p[0];
+ ph = h4ton3(ph, h->mlen);
- p = (u_char*)×tamp;
- *ph++ = p[2];
- *ph++ = p[1];
- *ph++ = p[0];
- *ph++ = p[3];
+ ph = h4ton3(ph, timestamp);
+ *ph++ = (u_char)(timestamp>>24);
*ph++ = 0;
*ph++ = 0;
@@ -985,12 +967,8 @@ ngx_rtmp_record_write_frame(ngx_rtmp_session_t *s,
/* write tag size */
ph = hdr;
- p = (u_char*)&tag_size;
-
- *ph++ = p[3];
- *ph++ = p[2];
- *ph++ = p[1];
- *ph++ = p[0];
+ *(uint32_t*)ph = htonl(tag_size);
+ ph += 4;
if (ngx_write_file(&rctx->file, hdr, ph - hdr,
rctx->file.offset)
diff --git a/ngx_rtmp_send.c b/ngx_rtmp_send.c
index 69dfed955..764e9ef6f 100644
--- a/ngx_rtmp_send.c
+++ b/ngx_rtmp_send.c
@@ -33,13 +33,13 @@
*(__b->last++) = (u_char)(utype);
#define NGX_RTMP_USER_OUT1(v) \
- *(__b->last++) = ((u_char*)&v)[0];
+ *(__b->last++) = (u_char) v;
#define NGX_RTMP_USER_OUT4(v) \
- *(__b->last++) = ((u_char*)&v)[3]; \
- *(__b->last++) = ((u_char*)&v)[2]; \
- *(__b->last++) = ((u_char*)&v)[1]; \
- *(__b->last++) = ((u_char*)&v)[0];
+ *(__b->last++) = (u_char) (v >> 24); \
+ *(__b->last++) = (u_char) (v >> 16); \
+ *(__b->last++) = (u_char) (v >> 8); \
+ *(__b->last++) = (u_char) v;
#define NGX_RTMP_USER_END(s) \
ngx_rtmp_prepare_message(s, &__h, NULL, __l); \