Compare commits

...

189 commits

Author SHA1 Message Date
Laura Hausmann a5f44b6d54
Correctly set media sequence number
https://datatracker.ietf.org/doc/html/rfc8216#section-4.3.3.2 specifies the media sequence number to be the number of the *first* fragment in the list, not the last one.
2024-01-06 01:16:31 +01:00
Laura Hausmann cfb277dea7
fix empty target header 2022-12-03 22:16:22 +01:00
Laura Hausmann d2d55348d6
simplify publish; add user header; fix module order for local redirects 2022-12-03 22:16:18 +01:00
jthomas 3b7ada6677
added multiple comma-separated push targets in on_publish http callback 2022-12-03 22:16:04 +01:00
Sergey Dryabzhinsky 22861b746d Version 1.2.2-r1 2022-05-15 14:53:16 +03:00
Sergey Dryabzhinsky 6e40dbe805
Merge pull request #346 from z411/fix-datetime
Fix hls_datetime not working after the first fragment
2022-05-15 13:48:34 +03:00
z411 5f8a96f505
Fix datetime not working after the first fragment 2022-05-15 04:32:56 -04:00
Sergey Dryabzhinsky 9102003adf
Merge pull request #336 from nextdayvideo/fix-record-interval-keyframe
For video streams, wait for a video keyframe before rotating the file.
2022-02-10 18:28:59 +03:00
Sergey Dryabzhinsky ca9fd4a380
Merge pull request #343 from zotanmew/patch-media-counter
Fix media sequence counter
2022-02-10 18:26:34 +03:00
Sergey Dryabzhinsky 0668f512b6
Merge pull request #342 from zotanmew/patch-hls-endlist
Write ENDLIST tag on stream end
2022-02-10 18:25:53 +03:00
Sergey Dryabzhinsky e57666bcf4
Merge pull request #337 from aminvakil/patch-1
minor typo
2022-02-10 18:25:00 +03:00
Laura Hausmann 2fee90d89f
fix media sequence counter 2022-02-09 23:19:59 +01:00
Laura Hausmann e73d44f0fb
Write ENDLIST tag on stream end 2022-02-09 23:08:41 +01:00
Amin Vakil 9eefedac83
minor typos 2021-09-15 12:30:08 +04:30
Amin Vakil c4dcf60e63
minor typo 2021-09-15 12:27:13 +04:30
Michael Farrell e8ff79dfb9 For video streams, wait for a video keyframe before rotating the file (record_interval).
Fixes https://github.com/sergey-dryabzhinsky/nginx-rtmp-module/issues/335
2021-07-17 17:19:04 +10:00
Sergey Dryabzhinsky 8e344d7994 For issue #332 - bigger buffers for HLS/DASH, allow define them on compile time 2021-06-03 15:40:00 +03:00
Sergey Dryabzhinsky 4d15e2c0f1 One more fix for fall through warnings 2021-06-03 12:51:03 +03:00
Sergey Dryabzhinsky d4d762e917
Merge pull request #331 from sergey-dryabzhinsky/issue-317-fix-double-ampersand-in-event-request
Issue 317 fix double ampersand in event request
2021-06-03 10:24:03 +03:00
Sergey Dryabzhinsky 5cb0be7f09 Fix warning-error about reproducible builds 2021-06-02 21:23:09 +03:00
Sergey Dryabzhinsky 1688a23f0a Simplify request string format 2021-06-02 21:08:37 +03:00
Sergey Dryabzhinsky a2895a03d9 Try to fix #317 - check for session vars and add ampersand only if they exists 2021-06-02 21:00:06 +03:00
Sergey Dryabzhinsky 649d220306 Version bump - merged all? fixes from arut 2021-06-02 20:45:44 +03:00
Sergey Dryabzhinsky ca1f3eeaa2 One more type cast 2021-06-02 20:43:18 +03:00
Sergey Dryabzhinsky a4a1343bb8
Merge pull request #176 from sergey-dryabzhinsky/fix-hls-playlist-length-on-varsize-fragments
Fix hls playlist length on varsize fragments
2021-06-01 19:06:41 +03:00
Sergey Dryabzhinsky 14221340d3 Fix miscast 2021-06-01 19:06:04 +03:00
Sergey Dryabzhinsky f7254ae5e8
Merge pull request #272 from eutychus/dev
Stats improvements
2021-06-01 18:59:59 +03:00
Sergey Dryabzhinsky fe35e3e98b
Merge pull request #329 from sergey-dryabzhinsky/merge-arut-122
Backport fixes from 1.2.2 by arut
2021-06-01 18:53:04 +03:00
Sergey Dryabzhinsky cdbb1d4dc1 Backport fixes from 1.2.2 by arut 2021-06-01 18:50:46 +03:00
root d743c36996 revert variant playlist code 2020-12-30 18:19:23 +00:00
root 59c2454b23 Revert "Intellegent variant playlist entries. Auto stream inf, skip if not live"
This reverts commit 7b7d30f36c.

Conflicts:
	hls/ngx_rtmp_hls_module.c
2020-12-30 04:01:12 +00:00
root 6c4be06423 Merge branch 'dev' of github.com:sergey-dryabzhinsky/nginx-rtmp-module into dev 2020-12-30 03:50:17 +00:00
Sergey Dryabzhinsky 23ec4ce2d7
Merge pull request #273 from baxerus/dev
Added millisecond accuracy to EXT-X-PROGRAM-DATE-TIME
2020-04-23 17:43:38 +03:00
Sergey Dryabzhinsky 882ef5ca1e
Merge pull request #294 from AdrianDroid/dev
Aglin case use with Adobe FMS
2020-04-23 17:25:13 +03:00
Sergey Dryabzhinsky eee3c5eb15
Merge pull request #307 from southernsun/patch-1
Fixed typo
2020-04-23 17:19:49 +03:00
southernsun a0a55be887
Fixed typo 2020-04-23 08:57:00 +02:00
adrian ce5a10a0d1 Aglin case use with Adobe FMS 2019-07-03 15:42:04 +12:00
Sergey Dryabzhinsky 3bf7523267
Merge pull request #293 from ssamjh/patch-2
Fix URL (attempt 2)
2019-05-04 11:13:59 +03:00
ssamjh b2049f3c39
Fix URL (attempt 2) 2019-05-04 13:55:18 +12:00
Sergey Dryabzhinsky a5ac72c274
Merge pull request #288 from asticode/patch-1
Fix fallthrough
2019-02-18 08:36:52 +03:00
Quentin Renard 00fd6cfa53 Fix fallthrough 2019-02-17 11:09:10 +01:00
Benedict Endemann b4ee055393 Added millisecond accuracy to EXT-X-PROGRAM-DATE-TIME
to become more compatible to:
https://tools.ietf.org/html/draft-pantos-http-live-streaming-23#section-4.3.2.6
(see "EXT-X-PROGRAM-DATE-TIME tags SHOULD provide millisecond accuracy.").
2018-04-23 12:53:42 +02:00
Brad Murray e2a626ac04 Code cleanup, change codec string for mp3 audio 2018-04-19 23:51:20 -04:00
Brad Murray 7b7d30f36c Intellegent variant playlist entries. Auto stream inf, skip if not live 2018-04-19 15:02:03 -04:00
Brad Murray e5b78f2de7 Merge branch 'dev' of https://github.com/eutychus/nginx-rtmp-module into dev 2018-04-16 18:13:19 -04:00
Brad Murray a65297410e track meta videokeyframe_frequency, add to stats 2018-04-16 18:08:23 -04:00
Brad Murray 669059f41b track meta videokeyframe_frequency, add to stats 2018-04-16 17:54:22 -04:00
Brad Murray ff3536996c Add metadata audio+video data rate to stats 2018-04-16 17:03:53 -04:00
Brad Murray 15cc5d0226 Increase length for flashver. It was truncated for wirecast. 2018-04-16 16:54:59 -04:00
Sergey Dryabzhinsky 504b9ee29d
Merge pull request #270 from benwilber/insert-datetime-before-discontinuity
Write EXT-X-PROGRAM-DATE-TIME before any EXT-X-DISCONTINUITY
2018-03-30 05:20:22 +03:00
Ben Wilber 23d67822b2 Write EXT-X-PROGRAM-DATE-TIME before any EXT-X-DISCONTINUITY 2018-03-29 16:49:53 -04:00
Sergey Dryabzhinsky 21db986d97
Merge pull request #260 from cryptofuture/dev
Fix multiple fallthrough warnings
2017-12-11 16:09:14 +03:00
Anonymous a01cc448ee
Fix multiple fallthrough warnings 2017-12-02 19:33:11 +00:00
Sergey Dryabzhinsky a898a09d87 Merge pull request #247 from heftig/master
Minor fixes
2017-05-23 13:40:46 +04:00
Jan Alexander Steffens (heftig) 916f3f8374
Simplify freeing the data chain 2017-05-23 10:11:27 +02:00
Jan Alexander Steffens (heftig) 1c3dc989ef
Work around deletion of still-needed init segments
I assume they get accidentally deleted right after they're
written, before the playlist is first created.

Leave them around for another cleanup cycle rather than
risk deleting them while still needed.
2017-05-23 10:11:27 +02:00
Sergey Dryabzhinsky d25c56fa69 Merge pull request #245 from sergey-dryabzhinsky/revert-199-drop-all-connections-on-reload
Revert "Add event-based handle of reload/exit signal"
2017-05-19 02:00:27 +04:00
Sergey Dryabzhinsky e65f2d099b Revert "Add event-based handle of reload/exit signal" 2017-05-19 00:59:11 +03:00
Sergey Dryabzhinsky f31e27fbaf Merge pull request #236 from Gamec/patch-1
EXT-X-ALLOW-CACHE using YES|NO instead of 1|0
2017-04-18 15:27:20 +03:00
Paweł Burda bb4190e248 EXT-X-ALLOW-CACHE using YES|NO instead of 1|0
https://tools.ietf.org/html/draft-pantos-http-live-streaming-07#section-3.3.6
2017-04-18 14:01:35 +02:00
Sergey 542106e4de Merge pull request #199 from sergey-dryabzhinsky/drop-all-connections-on-reload
Add event-based handle of reload/exit signal
2017-04-15 21:08:29 +03:00
Sergey 9121b34bdc Merge pull request #233 from mbeacom/openssl11
OpenSSL-1.1 support cherrypick
2017-04-14 20:04:06 +03:00
Sergey ff86f5c3fd Merge pull request #228 from securogroup/auto-crc-mpegts
Automatically generate CRC for MPEG-TS packets.
2017-04-13 23:11:58 +03:00
Roman Arutyunyan f23323a51a
OpenSSL-1.1 support 2017-04-13 15:12:47 -04:00
Chris Wiggins f8992e572f Automatically generate CRC for MPEG-TS packets.
Includes descriptions from @premultiply from pull sergey-dryabzhinsky/nginx-rtmp-module#123
2017-03-27 12:06:18 +13:00
Sergey 4975784d46 Merge pull request #217 from diegostamigni/record_started
`exec_record_started' call moved
2017-02-11 01:03:26 +03:00
Diego Stamigni 07912c5cd1 exec_record_started is now called when the first frame is written in file 2017-02-10 17:31:22 +00:00
Sergey 95d81573c9 Merge pull request #216 from diegostamigni/record_started
"exec_record_started" and "on_record_started" events added
2017-02-09 20:46:14 +03:00
Diego Stamigni bc81475b6b fixes for typo 2017-02-09 17:02:08 +00:00
Diego Stamigni 6b8155cf3b "exec_record_started" event and "on_record_started" notification added fired when the system starts the recording process 2017-02-09 13:47:29 +00:00
Sergey 9c71ce6761 Merge pull request #202 from fserreau/dev
Fixed record from infinite loop
2016-11-30 03:23:10 +03:00
Francois Serreau d86287fe3c Fixed record from infinite loop 2016-11-29 13:55:56 +01:00
Sergey Dryabzhinsky ebe697b601 Add event-based handle of reload/exit signal
- more useable types for session structure fields
- add event and timers to catch nginx exiting status
  and close all session / connections
  or main event loop will wait forever
2016-11-26 09:17:36 +03:00
Sergey Dryabzhinsky dc76eb2641 Merge fixes for nginx 1.11.5-style cache-manager by @arut
- merge some code from 5150993acc
2016-11-26 00:48:24 +03:00
Sergey Dryabzhinsky 18b228a01d Merge branch 'arut-1.1.9-fix-dynamic-module' into dev 2016-11-26 00:44:35 +03:00
Sergey Dryabzhinsky 4bf6852a28 Get changes from @arut c0bf381d10 2016-11-26 00:43:32 +03:00
Sergey 4809496d78 Merge pull request #198 from sergey-dryabzhinsky/akotulu-master
Merge fixes from @akotulu for #197
2016-11-26 00:29:09 +03:00
Sergey Dryabzhinsky 315e8aa497 Merge fixes from @akotulu for #197 2016-11-26 00:25:17 +03:00
Sergey dbcb7aa966 Merge pull request #184 from Stvad/dev
Infinite loop fix #145
2016-11-25 23:50:01 +03:00
Vladyslav Sitalo 2fd45d4114 metadata. infinite loop. #145 2016-09-12 16:27:32 +02:00
Sergey c47cb2370f Merge pull request #178 from sergey-dryabzhinsky/add-par-to-sar
Fix DASH playlist generation - validation check pass
2016-08-15 13:48:06 +03:00
Sergey Dryabzhinsky a037181c59 Fix DASH playlist generation - validation check pass
- add PAR value to AdaptationSet
- move UTCTiming tag to end of playlist
2016-08-07 09:43:34 +03:00
Sergey Dryabzhinsky 7381b66e13 typo 2016-08-06 07:43:20 +03:00
Sergey Dryabzhinsky a88bc39141 Fixes:
- fix first fragment search
- fix log output for discontinuety flag
2016-08-06 07:39:17 +03:00
Sergey Dryabzhinsky 998de2937a Try to coop with playlist length and fragments duration 2016-08-06 05:23:18 +03:00
Sergey a2d65b4251 Merge pull request #173 from heftig/master
Allow more than one option to 'listen'
2016-07-27 11:43:26 +04:00
Jan Alexander Steffens (heftig) 26d6107307 Allow more than one option to 'listen'
Without this patch, you wouldn't be able to specify e.g.
both so_keepalive and ipv6only, in conflict with the docs.
2016-07-27 09:20:21 +02:00
Sergey e38fcac9c9 Merge pull request #172 from sergey-dryabzhinsky/issue-145-infinite-loop
Hack for circular chain by @heftig
2016-07-27 00:27:19 +04:00
Sergey Dryabzhinsky e4799c633a Hack for circular chain by @heftig
- fix infinite loop on session close
2016-07-26 23:24:32 +03:00
Sergey 7db5ef0ea5 Merge pull request #138 from sergey-dryabzhinsky/fix-121-dash-availabilityEndTime
Rewrite availabilityStart and publish time generation to DASH manifest
2016-07-06 18:02:14 +03:00
Sergey a9e0056d5b Merge pull request #139 from sergey-dryabzhinsky/add-player-redirect-support
Add redirect support for playback notify
2016-06-26 20:19:24 +03:00
Sergey 1d5a20ea2b Merge pull request #160 from sergey-dryabzhinsky/issue-158-fix-windows-build-with-1.11-nginx
Type conversions for MSVS
2016-06-25 17:15:06 +03:00
Sergey Dryabzhinsky eca3fa3b04 Fix unused var warning for nginx < 1.11 2016-06-25 17:13:46 +03:00
Sergey Dryabzhinsky 2b0596051e Another type conversion fix for MSVS 2016-06-25 16:00:57 +03:00
Sergey Dryabzhinsky 77ba897d2f Another compiler warning fix 2016-06-25 13:55:08 +03:00
Sergey Dryabzhinsky 6d9a85e061 Type conversion
- remove double var definition
- add conversion to u_char for socketaddr
2016-06-24 00:00:14 +03:00
Sergey Dryabzhinsky b4ecd58544 Adjusted time roundup, time string formating 2016-06-20 14:53:09 +03:00
Sergey aee81e3c8f Merge pull request #150 from karlisk/dev
Updated README.md
2016-06-13 12:53:55 +03:00
Kārlis K 45a02da89e Updated README.md
Updated info about Multi-worker support, may require better detailed
explanation than provided.
2016-06-12 19:00:04 +03:00
Sergey 51396cdebb Merge pull request #148 from RocFang/dev
typo fix.
2016-06-10 16:25:40 +03:00
RocFang 358806e915 typo fix 2016-06-10 20:07:00 +08:00
RocFang 14b56c4a5b type error fix: accroding to the code details, ngx_rtmp_live_app_conf_t->buflen should be ngx_rtmp_live_app_conf_t->buffer. It's a flag rather than a msec config 2016-06-10 18:39:48 +08:00
Sergey 0df743179d Merge pull request #141 from RocFang/dev
compile with nginx-1.11.0
2016-05-31 11:22:43 +03:00
RocFang 965523f397 compile with nginx-1.11.0 2016-05-30 22:56:16 +08:00
Sergey fe122c1597 Merge pull request #137 from RocFang/patch-1
Update ngx_rtmp_core_module.c
2016-05-26 19:54:32 +03:00
RocFang a48dadfbc1 Update ngx_rtmp_core_module.c
clean useless code fragments.
2016-05-26 20:43:57 +08:00
Sergey Dryabzhinsky 341b07409d Update time generation:
- add msec granularity,
- revert availability constant change,
- add some time tricks to prevert playlist end on client too early
- add more debug to codecs
2016-05-24 15:13:24 +03:00
Sergey Dryabzhinsky 970da5673d Add new options for clock compensation tag
- new options, docs updated
- fix availability and publish time update
2016-05-24 04:34:16 +03:00
Sergey Dryabzhinsky 570204bdeb Try to update publishTime constantly with MPD updates 2016-05-24 00:59:15 +03:00
Sergey Dryabzhinsky 8b97be9593 Add availabilityEndTime to DASH manifest 2016-05-23 22:44:51 +03:00
Sergey Dryabzhinsky c3237ae747 Update README abount new dev branch 2016-05-16 11:35:24 +03:00
Sergey 62748fe56d Merge pull request #130 from securogroup/update-403-fix
Fix returning 40x on_update handler to correctly disconnect client
2016-05-13 23:20:41 +03:00
Chris Wiggins 1e6ae8d94d Fix returning 40x on_update handler to correctly disconnect client 2016-05-14 08:10:59 +12:00
Sergey 281d2226d9 Merge pull request #121 from ArhiChief/master
MPEG-DASH manifest generations update
2016-04-26 14:18:43 +03:00
ArhiChief 89dd74e666 Add publish time to MPD and UTCTiming element to force player to synchronize time with streaming server. 2016-04-25 15:54:29 +03:00
Sergey 4f96ff087d Merge pull request #119 from RocFang/bugfix
move ngx_events_module and ngx_event_core module to the posion before…
2016-04-23 00:48:26 +03:00
RocFang 16851c4512 move ngx_events_module and ngx_event_core module to the posion before modules introduced by nginx-rtmp in the ngx_modules array 2016-04-22 19:09:57 +08:00
Sergey 28f75cb86d Merge pull request #114 from sergey-dryabzhinsky/notify-reorder-internal-params-setup
Reorder notify url params setup
2016-04-22 09:47:42 +03:00
Sergey Dryabzhinsky 2a6b426247 Update notfy parameters handle:
- not pass empty session params
- do pass them into last request chunk
2016-04-22 01:00:46 +03:00
Sergey Dryabzhinsky d171a0a9b0 Reorder ampersand 2016-04-21 23:00:37 +03:00
Sergey Dryabzhinsky 0d94bb2c84 Return on_connect construct params from context 2016-04-21 23:00:37 +03:00
Sergey Dryabzhinsky 0bd7d6b375 Fix notify request data:
- loosen client args
- cleanup data - app name and other doubles
- connect data via create_request
2016-04-21 23:00:37 +03:00
Sergey Dryabzhinsky 307c8d969a Max request length and args length increase 2016-04-21 23:00:37 +03:00
Sergey Dryabzhinsky 93e9377dc6 Reorder notify url params setup
- move clients first
- rewrite them by internal ones
2016-04-21 23:00:37 +03:00
Sergey 5376bd3432 Merge pull request #116 from heftig/master
dash: Ensure directory exists before opening fragments
2016-04-21 13:34:17 +03:00
Jan Alexander Steffens (heftig) 98f700a090 dash: Ensure directory exists before opening fragments
Streams that take a long time from initial Publish to
actually sending data might get their directories reaped.

Testcase: Xsplit Encoder with stream delay set to a value larger
than the playlist length.
2016-04-21 12:22:03 +02:00
Sergey Dryabzhinsky 0bfbd6b39f Forgot to commit main redirect code 2016-04-20 09:27:34 +03:00
Sergey f15596b8d1 Merge pull request #113 from RocFang/patch-1
bugfix of stream_buckets
2016-04-19 06:30:13 +03:00
Sergey 64c0529fde Merge pull request #112 from benmcmeen/master
HLS Live DVR Flag
2016-04-19 06:24:04 +03:00
RocFang 01825510f7 bugfix of stream_buckets
This bug will lead to the failure of setting ngx_rtmp_live_app_conf_t->nbuckets.

by default it is 1024,but if you set it specificly, it will be wrong.
2016-04-19 10:42:13 +08:00
benmcmeen 93cf3b69f1 Docs 2016-04-18 14:21:56 -05:00
benmcmeen bfaccfd738 More compilation. 2016-04-18 13:48:40 -05:00
benmcmeen 7eb100a306 Compilation 2016-04-18 13:34:24 -05:00
benmcmeen a3924dce67 Modified flag, enum. 2016-04-18 12:46:26 -05:00
benmcmeen 66b3bcf096 Housekeeping. 2016-04-15 13:50:29 -05:00
benmcmeen 65e24b3fee Added struct 2016-04-15 13:19:43 -05:00
benmcmeen 182566fe93 Fixed variable error. 2016-04-15 13:16:45 -05:00
benmcmeen e8304c9852 HLS Live DVR flag. 2016-04-15 13:10:29 -05:00
Sergey 7e68afde6f Merge pull request #109 from securogroup/no-cc-hls
Specifically state that we dont support HLS closed captions
2016-04-15 07:55:02 +03:00
Chris Wiggins d13e665e56 Specifically state that we dont support HLS closed captions 2016-04-15 09:23:14 +12:00
Sergey 2855a9ffc1 Merge pull request #107 from securogroup/single-track-fix
Populate single-audio track header correctly
2016-04-14 10:02:44 +03:00
Chris Wiggins 86cfd20b28 Populate singe-audio track header correctly 2016-04-14 10:24:38 +12:00
Sergey cfadbd7779 Merge pull request #106 from securogroup/single-track-fix
Single track fix
2016-04-13 14:02:43 +03:00
Chris Wiggins c11797815d Fix single-track HLS MPEG-TS streams 2016-04-13 17:44:39 +12:00
Sergey 6666d789b5 Merge pull request #104 from denji/ngx_dynamic
Support Dynamic Module NGINX ≥ 1.9.11
2016-04-08 09:37:58 +03:00
Denis Denisov ede4b5f0f4 Support Dynamic Module NGINX ≥ 1.9.11 2016-04-08 04:19:46 +03:00
Sergey Dryabzhinsky 9f75cc2c6e Next dev version 2016-03-01 03:27:06 +03:00
Sergey 2d4613c906 Merge pull request #89 from sergey-dryabzhinsky/74-fix-metadata
Revert previous fix.
2016-02-28 02:15:44 +03:00
Sergey Dryabzhinsky fc013040b6 Revert previous fix. 2016-02-28 02:13:07 +03:00
Sergey 12595a21aa Merge pull request #88 from sergey-dryabzhinsky/74-fix-metadata
Wrong lines removed. Fix it. Also change link to project in info.
2016-02-28 01:26:09 +03:00
Sergey Dryabzhinsky f9d89634ad Wrong lines removed. Fix it. Also change link to project in info. 2016-02-28 01:23:11 +03:00
Sergey f344f4ae92 Merge pull request #86 from sergey-dryabzhinsky/78-support-build-as-module
Fix build with nginx versions older than 1.9.11
2016-02-27 22:10:58 +03:00
Sergey Dryabzhinsky d28e52b32b Fix build with nginx versions older than 1.9.11 2016-02-27 22:09:26 +03:00
Sergey c0b592a57c Merge pull request #84 from sergey-dryabzhinsky/64i-mpegts-continuity-counter-fix
Update the continuity_counter for packets pat and pmt
2016-02-27 21:03:47 +03:00
Sergey 5e179d7296 Merge pull request #83 from sergey-dryabzhinsky/82-fix-playlists-cleanup
Fix HLS/DASH playlists cleanup
2016-02-27 21:01:32 +03:00
Sergey Dryabzhinsky 4ce7ea8b9d Fix HLS/DASH playlists cleanup
- use x2 hls max frgment duration
- do more recent cleanup - half playlist duration
- adjust expiration times for playlist files
- add more debug to dash fragments update function
2016-02-27 07:06:16 +03:00
Sergey 292a6c1ca8 Merge pull request #81 from datarhei/wiki-to-docs
MOD move wiki to /docs
2016-02-27 01:53:40 +03:00
Jan Stabenow f89d8c1973 FIX missing readme 2016-02-26 23:33:42 +01:00
Sergey Dryabzhinsky 96b69327fa Update the continuity_counter for packets pat and pmt 2016-02-27 01:13:22 +03:00
Jan Stabenow 298697a4da MOD move wiki to /docs 2016-02-26 22:42:10 +01:00
Sergey bf332f3794 Merge pull request #79 from sergey-dryabzhinsky/78-support-build-as-module
Build ngx_rtmp_module as a dynamic module
2016-02-26 21:11:35 +03:00
Sergey Dryabzhinsky a194707ea9 Build ngx_rtmp_module as a dynamic module 2016-02-26 21:05:43 +03:00
Sergey cd0d9f73a5 Merge pull request #77 from sergey-dryabzhinsky/76-bitrate-in-double
Store audio and video bitrate in variables of type double
2016-02-26 20:46:53 +03:00
Sergey Dryabzhinsky b9bdd89676 Store audio and video bitrate in variables of type double
Bitrate's fraction part is lost when casting to integer type, so it
should be stored in double.
2016-02-26 20:44:48 +03:00
Sergey 07a83d8750 Merge pull request #75 from sergey-dryabzhinsky/74-fix-metadata
Correctly parse meta data
2016-02-26 20:38:46 +03:00
Sergey Dryabzhinsky b937376041 Flash video stream's meta data (onMetaData) contains only an object (and
no string before it), so read the object only.
2016-02-26 20:36:25 +03:00
Sergey Dryabzhinsky 1b7d6148e5 Append .gitignore 2016-02-26 20:33:29 +03:00
Sergey Dryabzhinsky 63c87e8070 Connection info not always available. Show zero port if not. 2016-01-31 18:29:02 +03:00
Sergey Dryabzhinsky 8c5139650d Use nginx interval functions, Remove unused var 2016-01-05 21:54:13 +03:00
Sergey 94343ad786 Merge pull request #61 from sergey-dryabzhinsky/i60-fix-build-on-windows-timeh-missing
Use internal nginx functions, fix time format, use numeric debug logger functions
2015-12-25 21:46:27 +03:00
Sergey Dryabzhinsky 3352af8b73 More fixes - debug functions 2015-12-24 21:19:39 +03:00
Sergey Dryabzhinsky 118b808207 Update:
- use arg-count-named debug functions
- fix void result use
2015-12-24 14:43:37 +03:00
Sergey Dryabzhinsky dc5add30a8 Use debug level for debug messages 2015-12-24 13:04:20 +03:00
Sergey Dryabzhinsky c206cd7978 Use internal nginx functions, fix time format 2015-12-24 12:33:50 +03:00
Sergey 9b25e901f8 Merge pull request #58 from sergey-dryabzhinsky/notify-on-playlist-hls-dash
Add playlist update notification for HLS/DASH
2015-12-17 15:48:10 +03:00
Sergey Dryabzhinsky f8da609671 Add playlist update notification for HLS/DASH 2015-12-17 03:36:58 +03:00
Sergey Dryabzhinsky 06e49e05fd Forget to uncomment functions 2015-11-30 20:52:18 +03:00
Sergey Dryabzhinsky 4c7dd6ed00 Update:
- add more debug output for 'play' handler to view all chained calls
- fix gcc compilation fail or live module with ```-Werror``` option
  - wrap var definition with debug enabled check
2015-11-30 13:02:57 +03:00
Sergey c80342e0ab Merge pull request #50 from sergey-dryabzhinsky/on-done-events-send-bytes
On done events send bytes stats
2015-11-12 09:55:12 +03:00
Sergey Dryabzhinsky b6687d6cab Update CRC for MP3 2015-11-11 20:22:48 +03:00
Sergey d1e9d8682e Merge pull request #51 from premultiply/patch-1
MPEG TS improvements
2015-11-11 18:54:53 +03:00
premultiply 2bc42d90f2 MPEG TS improvements
- Changed PMT PID to widely used 0x0FFF (4095)
- Write PCR on every frames (for hardware decoders and other DVB compatible decoders)
- Recalculated PAT CRC32

TODO: Recalculate PMT CRC32
2015-11-11 15:31:05 +01:00
Sergey Dryabzhinsky 9ec1c78749 Add input/output bytes stats into notify (on_*_done) data 2015-10-27 16:46:25 +03:00
Sergey Dryabzhinsky cd416d5fd2 Fix gcc warning 'value computed is not used' 2015-10-27 16:43:06 +03:00
Sergey 74fdeef568 Merge pull request #48 from mattpepin/master
Freeze availabilityStartTime and adjust timeShiftBufferDepth
2015-10-24 00:22:48 +03:00
Matthieu Pepin 0f73f4e05b Style (use spaces instead of tabs) 2015-10-23 13:14:55 -04:00
Matthieu Pepin f9aa396689 Add year/month/days to timeShiftBufferDepth 2015-10-23 13:12:01 -04:00
Matthieu Pepin fd23b27f3f Fix decimal places (msec, only 2 allowed) 2015-10-23 10:58:52 -04:00
Matthieu Pepin c078a7c3e0 Freeze availabilityStartTime and adjust timeShiftBufferDepth 2015-10-22 17:34:47 -04:00
48 changed files with 4193 additions and 568 deletions

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
/.idea
/.settings
/.project
/.cproject
/.vscode

View file

@ -1,14 +1,29 @@
# NGINX-based Media Streaming Server
## nginx-rtmp-module
## nginx-rtmp-module
### Project blog
http://nginx-rtmp.blogspot.com
### Wiki manual
### Documentation
https://github.com/arut/nginx-rtmp-module/wiki/Directives
* [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
@ -76,6 +91,12 @@ For building debug version of nginx add `--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
@ -97,10 +118,8 @@ name - interpreted by each application
### Multi-worker live streaming
Module supports multi-worker live
streaming through automatic stream pushing
to nginx workers. This option is toggled with
rtmp_auto_push directive.
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
@ -138,7 +157,7 @@ rtmp_auto_push directive.
application big {
live on;
# On every pusblished stream run this command (ffmpeg)
# On every published stream run this command (ffmpeg)
# with substitutions: $app/${app}, $name/${name} for application & stream name.
#
# This ffmpeg call receives stream from this application &
@ -315,18 +334,3 @@ rtmp_auto_push directive.
}
}
}
### Multi-worker streaming example
rtmp_auto_push on;
rtmp {
server {
listen 1935;
application mytv {
live on;
}
}
}

58
config
View file

@ -1,6 +1,5 @@
ngx_addon_name="ngx_rtmp_module"
CORE_MODULES="$CORE_MODULES
RTMP_CORE_MODULES=" \
ngx_rtmp_module \
ngx_rtmp_core_module \
ngx_rtmp_cmd_module \
@ -15,21 +14,18 @@ CORE_MODULES="$CORE_MODULES
ngx_rtmp_relay_module \
ngx_rtmp_exec_module \
ngx_rtmp_auto_push_module \
ngx_rtmp_notify_module \
ngx_rtmp_auto_push_index_module \
ngx_rtmp_log_module \
ngx_rtmp_limit_module \
ngx_rtmp_hls_module \
ngx_rtmp_dash_module \
ngx_rtmp_notify_module \
"
HTTP_MODULES="$HTTP_MODULES \
RTMP_HTTP_MODULES=" \
ngx_rtmp_stat_module \
ngx_rtmp_control_module \
"
NGX_ADDON_DEPS="$NGX_ADDON_DEPS \
RTMP_DEPS=" \
$ngx_addon_dir/ngx_rtmp_amf.h \
$ngx_addon_dir/ngx_rtmp_bandwidth.h \
$ngx_addon_dir/ngx_rtmp_cmd_module.h \
@ -48,9 +44,7 @@ NGX_ADDON_DEPS="$NGX_ADDON_DEPS \
$ngx_addon_dir/hls/ngx_rtmp_mpegts.h \
$ngx_addon_dir/dash/ngx_rtmp_mp4.h \
"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS \
RTMP_CORE_SRCS=" \
$ngx_addon_dir/ngx_rtmp.c \
$ngx_addon_dir/ngx_rtmp_init.c \
$ngx_addon_dir/ngx_rtmp_handshake.c \
@ -70,13 +64,10 @@ NGX_ADDON_SRCS="$NGX_ADDON_SRCS \
$ngx_addon_dir/ngx_rtmp_flv_module.c \
$ngx_addon_dir/ngx_rtmp_mp4_module.c \
$ngx_addon_dir/ngx_rtmp_netcall_module.c \
$ngx_addon_dir/ngx_rtmp_stat_module.c \
$ngx_addon_dir/ngx_rtmp_control_module.c \
$ngx_addon_dir/ngx_rtmp_relay_module.c \
$ngx_addon_dir/ngx_rtmp_bandwidth.c \
$ngx_addon_dir/ngx_rtmp_exec_module.c \
$ngx_addon_dir/ngx_rtmp_auto_push_module.c \
$ngx_addon_dir/ngx_rtmp_notify_module.c \
$ngx_addon_dir/ngx_rtmp_log_module.c \
$ngx_addon_dir/ngx_rtmp_limit_module.c \
$ngx_addon_dir/ngx_rtmp_bitop.c \
@ -84,11 +75,44 @@ NGX_ADDON_SRCS="$NGX_ADDON_SRCS \
$ngx_addon_dir/hls/ngx_rtmp_hls_module.c \
$ngx_addon_dir/dash/ngx_rtmp_dash_module.c \
$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/ngx_rtmp_notify_module.c \
"
RTMP_HTTP_SRCS=" \
$ngx_addon_dir/ngx_rtmp_stat_module.c \
$ngx_addon_dir/ngx_rtmp_control_module.c \
"
ngx_module_incs=$ngx_addon_dir
ngx_module_deps=$RTMP_DEPS
if [ "$ngx_module_link" = "" ] ; then
# Old nginx version
ngx_module_link=NONE
EVENT_MODULES="$EVENT_MODULES $RTMP_CORE_MODULES"
HTTP_MODULES="$HTTP_MODULES $RTMP_HTTP_MODULES"
NGX_ADDON_DEPS="$NGX_ADDON_DEPS $RTMP_DEPS"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $RTMP_CORE_SRCS $RTMP_HTTP_SRCS"
fi
if [ $ngx_module_link = DYNAMIC ] ; then
ngx_module_name="$RTMP_CORE_MODULES $RTMP_HTTP_MODULES"
ngx_module_srcs="$RTMP_CORE_SRCS $RTMP_HTTP_SRCS"
. auto/module
elif [ $ngx_module_link = ADDON ] ; then
ngx_module_type=EVENT
ngx_module_name=$RTMP_CORE_MODULES
ngx_module_srcs=$RTMP_CORE_SRCS
. auto/module
ngx_module_type=HTTP
ngx_module_name=$RTMP_HTTP_MODULES
ngx_module_srcs=$RTMP_HTTP_SRCS
. auto/module
fi
USE_OPENSSL=YES
CFLAGS="$CFLAGS -I$ngx_addon_dir"
# Debug build with all warnings as errors
# CFLAGS="$CFLAGS -I$ngx_addon_dir -Wall -Wpointer-arith -Wno-unused-parameter -Werror"
USE_OPENSSL=YES

View file

@ -12,6 +12,7 @@ static ngx_rtmp_publish_pt next_publish;
static ngx_rtmp_close_stream_pt next_close_stream;
static ngx_rtmp_stream_begin_pt next_stream_begin;
static ngx_rtmp_stream_eof_pt next_stream_eof;
static ngx_rtmp_playlist_pt next_playlist;
static ngx_int_t ngx_rtmp_dash_postconfiguration(ngx_conf_t *cf);
@ -19,13 +20,18 @@ 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_ensure_directory(ngx_rtmp_session_t *s);
#define NGX_RTMP_DASH_BUFSIZE (1024*1024)
#define NGX_RTMP_DASH_MAX_MDAT (10*1024*1024)
/* Big buffer for 8k (QHD) cameras */
#ifndef NGX_RTMP_DASH_BUFSIZE
#define NGX_RTMP_DASH_BUFSIZE (16*1024*1024)
#endif
#define NGX_RTMP_DASH_MAX_MDAT (16*1024*1024)
#define NGX_RTMP_DASH_MAX_SAMPLES 1024
#define NGX_RTMP_DASH_DIR_ACCESS 0744
/* Allow access to www-data (web-server) and others too */
#define NGX_RTMP_DASH_DIR_ACCESS 0755
#define NGX_RTMP_DASH_GMT_LENGTH sizeof("1970-09-28T12:00:00+06:00")
typedef struct {
uint32_t timestamp;
@ -78,11 +84,29 @@ typedef struct {
} ngx_rtmp_dash_cleanup_t;
#define NGX_RTMP_DASH_CLOCK_COMPENSATION_OFF 1
#define NGX_RTMP_DASH_CLOCK_COMPENSATION_NTP 2
#define NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_HEAD 3
#define NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_ISO 4
static ngx_conf_enum_t ngx_rtmp_dash_clock_compensation_type_slots[] = {
{ ngx_string("off"), NGX_RTMP_DASH_CLOCK_COMPENSATION_OFF },
{ ngx_string("ntp"), NGX_RTMP_DASH_CLOCK_COMPENSATION_NTP },
{ ngx_string("http_head"), NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_HEAD },
{ ngx_string("http_iso"), NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_ISO },
{ ngx_null_string, 0 }
};
typedef struct {
ngx_flag_t dash;
ngx_msec_t fraglen;
ngx_msec_t playlen;
ngx_flag_t nested;
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
// - same machine as RTMP/DASH)
// - or NTP server address
ngx_str_t path;
ngx_uint_t winfrags;
ngx_flag_t cleanup;
@ -134,6 +158,20 @@ static ngx_command_t ngx_rtmp_dash_commands[] = {
offsetof(ngx_rtmp_dash_app_conf_t, nested),
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,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_rtmp_dash_app_conf_t, clock_compensation),
&ngx_rtmp_dash_clock_compensation_type_slots },
{ ngx_string("dash_clock_helper_uri"),
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, clock_helper_uri),
NULL },
ngx_null_command
};
@ -212,6 +250,22 @@ ngx_rtmp_dash_rename_file(u_char *src, u_char *dst)
}
static ngx_uint_t
ngx_rtmp_dash_gcd(ngx_uint_t m, ngx_uint_t n)
{
/* greatest common divisor */
ngx_uint_t temp;
while (n) {
temp=n;
n=m % n;
m=temp;
}
return m;
}
static ngx_int_t
ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
{
@ -222,14 +276,22 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
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 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_playlist_t v;
static u_char buffer[NGX_RTMP_DASH_BUFSIZE];
static u_char start_time[sizeof("1970-09-28T12:00:00+06:00")];
static u_char end_time[sizeof("1970-09-28T12:00:00+06:00")];
static u_char avaliable_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];
dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module);
@ -260,25 +322,29 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
" type=\"dynamic\"\n" \
" xmlns=\"urn:mpeg:dash:schema:mpd:2011\"\n" \
" availabilityStartTime=\"%s\"\n" \
" availabilityEndTime=\"%s\"\n" \
" minimumUpdatePeriod=\"PT%uiS\"\n" \
" minBufferTime=\"PT%uiS\"\n" \
" timeShiftBufferDepth=\"PT0H0M0.00S\"\n" \
" suggestedPresentationDelay=\"PT%uiS\"\n" \
" publishTime=\"%s\"\n" \
" minimumUpdatePeriod=\"PT%ui.%03uiS\"\n" \
" minBufferTime=\"PT%ui.%03uiS\"\n" \
" timeShiftBufferDepth=\"%s\"\n" \
" suggestedPresentationDelay=\"PT%ui.%03uiS\"\n" \
" profiles=\"urn:hbbtv:dash:profile:isoff-live:2012," \
"urn:mpeg:dash:profile:isoff-live:2011\"\n" \
" xmlns:xsi=\"http://www.w3.org/2011/XMLSchema-instance\"\n" \
" xsi:schemaLocation=\"urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd\">\n" \
" xsi:schemaLocation=\"urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd\">\n"
#define NGX_RTMP_DASH_MANIFEST_PERIOD \
" <Period start=\"PT0S\" id=\"dash\">\n"
#define NGX_RTMP_DASH_MANIFEST_VIDEO \
" <AdaptationSet\n" \
" id=\"1\"\n" \
" startWithSAP=\"1\"\n" \
" segmentAlignment=\"true\"\n" \
" maxWidth=\"%ui\"\n" \
" maxHeight=\"%ui\"\n" \
" maxFrameRate=\"%s\">\n" \
" maxFrameRate=\"%s\"\n" \
" par=\"%ui:%ui\">\n" \
" <Representation\n" \
" id=\"%V_H264\"\n" \
" mimeType=\"video/mp4\"\n" \
@ -287,7 +353,6 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
" height=\"%ui\"\n" \
" frameRate=\"%s\"\n" \
" sar=\"1:1\"\n" \
" startWithSAP=\"1\"\n" \
" bandwidth=\"%ui\">\n" \
" <SegmentTemplate\n" \
" presentationTimeOffset=\"0\"\n" \
@ -311,6 +376,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
#define NGX_RTMP_DASH_MANIFEST_AUDIO \
" <AdaptationSet\n" \
" id=\"2\"\n" \
" startWithSAP=\"1\"\n" \
" segmentAlignment=\"true\">\n" \
" <AudioChannelConfiguration\n" \
" schemeIdUri=\"urn:mpeg:dash:" \
@ -321,7 +387,6 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
" mimeType=\"audio/mp4\"\n" \
" codecs=\"mp4a.%s\"\n" \
" audioSamplingRate=\"%ui\"\n" \
" startWithSAP=\"1\"\n" \
" bandwidth=\"%ui\">\n" \
" <SegmentTemplate\n" \
" presentationTimeOffset=\"0\"\n" \
@ -338,42 +403,101 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
" </AdaptationSet>\n"
#define NGX_RTMP_DASH_PERIOD_FOOTER \
" </Period>\n"
#define NGX_RTMP_DASH_MANIFEST_CLOCK \
" <UTCTiming schemeIdUri=\"urn:mpeg:dash:utc:%s:2014\"\n" \
" value=\"%V\" />\n"
#define NGX_RTMP_DASH_MANIFEST_FOOTER \
" </Period>\n" \
"</MPD>\n"
ngx_libc_localtime(ctx->start_time.sec +
ngx_rtmp_dash_get_frag(s, 0)->timestamp / 1000, &tm);
*ngx_sprintf(start_time, "%4d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
tm.tm_year + 1900, tm.tm_mon + 1,
tm.tm_mday, tm.tm_hour,
/**
* 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",
tm.tm_year + 1900, tm.tm_mon + 1,
tm.tm_mday, tm.tm_hour,
tm.tm_min, tm.tm_sec
) = 0;
/* Stream publish time */
*ngx_sprintf(publish_time, "%s", avaliable_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,
ctx->start_time.gmtoff < 0 ? '-' : '+',
ngx_abs(ctx->start_time.gmtoff / 60),
ngx_abs(ctx->start_time.gmtoff % 60)) = 0;
ngx_libc_localtime(ctx->start_time.sec +
(ngx_rtmp_dash_get_frag(s, ctx->nfrags - 1)->timestamp +
ngx_rtmp_dash_get_frag(s, ctx->nfrags - 1)->duration) /
1000, &tm);
*ngx_sprintf(end_time, "%4d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
tm.tm_year + 1900, tm.tm_mon + 1,
tm.tm_mday, tm.tm_hour,
tm.tm_min, tm.tm_sec,
ctx->start_time.gmtoff < 0 ? '-' : '+',
ngx_abs(ctx->start_time.gmtoff / 60),
ngx_abs(ctx->start_time.gmtoff % 60)) = 0;
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,
start_time,
end_time,
(ngx_uint_t) (dacf->fraglen / 1000),
(ngx_uint_t) (dacf->fraglen / 1000),
(ngx_uint_t) (dacf->fraglen / 500));
// availabilityStartTime
avaliable_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);
@ -407,10 +531,15 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
*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_VIDEO,
codec_ctx->width,
codec_ctx->height,
frame_rate,
par_x, par_y,
&ctx->name,
codec_ctx->avc_profile,
codec_ctx->avc_compat,
@ -454,6 +583,34 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
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);
@ -475,7 +632,11 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
return NGX_ERROR;
}
return NGX_OK;
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);
}
@ -750,6 +911,10 @@ ngx_rtmp_dash_open_fragments(ngx_rtmp_session_t *s)
return NGX_OK;
}
if (ngx_rtmp_dash_ensure_directory(s) != NGX_OK) {
return NGX_ERROR;
}
ngx_rtmp_dash_open_fragment(s, &ctx->video, ctx->id, 'v');
ngx_rtmp_dash_open_fragment(s, &ctx->audio, ctx->id, 'a');
@ -1026,6 +1191,10 @@ ngx_rtmp_dash_update_fragments(ngx_rtmp_session_t *s, ngx_int_t boundary,
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module);
f = ngx_rtmp_dash_get_frag(s, ctx->nfrags);
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"dash: update_fragments: timestamp=%ui, f-timestamp=%ui, boundary=%i, dacf-fraglen=%ui",
timestamp, f->timestamp, boundary, dacf->fraglen);
d = (int32_t) (timestamp - f->timestamp);
if (d >= 0) {
@ -1040,26 +1209,44 @@ ngx_rtmp_dash_update_fragments(ngx_rtmp_session_t *s, ngx_int_t boundary,
hit = (-d > 1000);
}
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"dash: update_fragments: d=%i, f-duration=%ui, hit=%i",
d, f->duration, hit);
if (ctx->has_video && !hit) {
boundary = 0;
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"dash: update_fragments: boundary=0 cos has_video && !hit");
}
if (!ctx->has_video && ctx->has_audio) {
boundary = hit;
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"dash: update_fragments: boundary=hit cos !has_video && has_audio");
}
if (ctx->audio.mdat_size >= NGX_RTMP_DASH_MAX_MDAT) {
boundary = 1;
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"dash: update_fragments: boundary=1 cos audio max mdat");
}
if (ctx->video.mdat_size >= NGX_RTMP_DASH_MAX_MDAT) {
boundary = 1;
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"dash: update_fragments: boundary=1 cos video max mdat");
}
if (!ctx->opened) {
boundary = 1;
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"dash: update_fragments: boundary=1 cos !opened");
}
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"dash: update_fragments: boundary=%i",
boundary);
if (boundary) {
ngx_rtmp_dash_close_fragments(s);
ngx_rtmp_dash_open_fragments(s);
@ -1381,7 +1568,7 @@ ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen)
"dash: cleanup '%V' allowed, mpd missing '%s'",
&name, mpd_path);
max_age = 0;
max_age = playlen / 500;
} else if (name.len >= 4 && name.data[name.len - 4] == '.' &&
name.data[name.len - 3] == 'm' &&
@ -1409,7 +1596,7 @@ ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen)
name.data[name.len - 2] == 'a' &&
name.data[name.len - 1] == 'w')
{
max_age = playlen / 1000;
max_age = playlen / 500;
} else {
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0,
@ -1437,17 +1624,31 @@ ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen)
}
}
#if (nginx_version >= 1011005)
static ngx_msec_t
#else
static time_t
#endif
ngx_rtmp_dash_cleanup(void *data)
{
ngx_rtmp_dash_cleanup_t *cleanup = data;
ngx_rtmp_dash_cleanup_dir(&cleanup->path, cleanup->playlen);
// Next callback in doubled playlist length time to make sure what all
// players read all segments
#if (nginx_version >= 1011005)
return cleanup->playlen * 2;
#else
return cleanup->playlen / 500;
#endif
}
static ngx_int_t
ngx_rtmp_dash_playlist(ngx_rtmp_session_t *s, ngx_rtmp_playlist_t *v)
{
return next_playlist(s, v);
}
static void *
ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf)
@ -1464,6 +1665,7 @@ 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->clock_compensation = NGX_CONF_UNSET;
return conf;
}
@ -1481,6 +1683,9 @@ 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_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, "");
if (conf->fraglen) {
conf->winfrags = conf->playlen / conf->fraglen;
@ -1549,5 +1754,8 @@ ngx_rtmp_dash_postconfiguration(ngx_conf_t *cf)
next_stream_eof = ngx_rtmp_stream_eof;
ngx_rtmp_stream_eof = ngx_rtmp_dash_stream_eof;
next_playlist = ngx_rtmp_playlist;
ngx_rtmp_playlist = ngx_rtmp_dash_playlist;
return NGX_OK;
}

View file

@ -1,2 +1,29 @@
Documentation is available here:
https://github.com/arut/nginx-rtmp-module/wiki
# Welcome to the nginx-rtmp-module documentation!
## Pages
* [Control module](control_modul.md)
* [Debug log](debug_log.md)
* [Directives](directives.md)
* [Examples](examples.md)
* [Exec wrapper in bash](exec_wrapper_in_bash.md)
* [FAQ](faq.md)
* [Getting number of subscribers](getting_number_of_subscribers.md)
* [Getting started with nginx rtmp](getting_started.md)
* [Installing in Gentoo](Installing_in_gentoo.md)
* [Installing on Ubuntu using PPAs](installing_ubuntu_using_ppas.md)
* [Tutorial](tutorial.md)
*Source: https://github.com/arut/nginx-rtmp-module/wiki*
## Updates
* Directives
* Notify
* [on_playlist](directives.md#on_playlist)
* [notify_send_redirect](directives.md#notify_send_redirect)
* Client Caching
* [hls_allow_client_cache](directives.md#hls_allow_client_cache)
* Dash MPD generation
* [dash_clock_compensation](directives.md#dash_clock_compensation)
* [dash_clock_helper_uri](directives.md#dash_clock_helper_uri)

94
doc/control_modul.md Normal file
View file

@ -0,0 +1,94 @@
# Control module
Control module is HTTP module which makes it possible to control rtmp module from outside using HTTP protocol. Here's an example of how to enable control.
```sh
http {
...
server {
listen 8080;
server_name localhost;
....
location /control {
rtmp_control all;
}
}
}
```
There are several sub-modules within control module each controlling a different feature.
# Record
This sub-module starts and stops recordings created with _manual_ flag.
Syntax:
```sh
http://server.com/control/record/start|stop?srv=SRV&app=APP&name=NAME&rec=REC
```
* srv=SRV - optional server{} block number within rtmp{} block, default to first server{} block
* app=APP - required application name
* name=NAME - required stream name
* rec=REC - optional recorder name, defaults to root (unnamed) recorder
Example
```sh
rtmp {
server {
listen 1935;
application myapp {
live on;
recorder rec1 {
record all manual;
record_suffix all.flv;
record_path /tmp/rec;
record_unique on;
}
}
}
}
```
Publish the stream with the following command
```sh
$ ffmpeg -i http://someserver.com/mychannel.ts -c:v copy -c:a nellymoser -ar 44100 -ac 1 -f flv rtmp://localhost/myapp/mystream
```
Use the following commands to start and stop recording
```sh
$ curl "http://localhost:8080/control/record/start?app=myapp&name=mystream&rec=rec1"
§ curl "http://localhost:8080/control/record/stop?app=myapp&name=mystream&rec=rec1"
```
if the record start/stop request returns nothing sometimes, you should check if you use multi workers. one worker works great.
# Drop
This sub-module provides a simple way to drop client connection.
Syntax:
```sh
http://server.com/control/drop/publisher|subscriber|client?
srv=SRV&app=APP&name=NAME&addr=ADDR&clientid=CLIENTID
```
* srv, app, name - the same as above
* addr - optional client address (the same as returned by rtmp_stat)
* clientid - optional nginx client id (displayed in log and stat)
The first method ```drop/publisher``` drops publisher connection. The second ```drop/client``` drops every connection matching ```addr``` argument or all clients (including publisher) if ```addr``` is not specified.
Examples
```sh
$ curl http://localhost:8080/control/drop/publisher?app=myapp&name=mystream
$ curl http://localhost:8080/control/drop/client?app=myapp&name=mystream
$ curl http://localhost:8080/control/drop/client?app=myapp&name=mystream&addr=192.168.0.1
$ curl http://localhost:8080/control/drop/client?app=myapp&name=mystream&clientid=1
```
# Redirect
Redirect play/publish client to a new stream.
Syntax:
```sh
http://server.com/control/redirect/publisher|subscriber|client?
srv=SRV&app=APP&name=NAME&addr=ADDR&clientid=CLIENTID&newname=NEWNAME
```
* srv, app, name, addr, clients - the same as above
* newname - new stream name to redirect to

16
doc/debug_log.md Normal file
View file

@ -0,0 +1,16 @@
# Debug log
In case you need to solve a streaming problem you might need to watch debug log.
For that configure nginx with *--with-debug* flag.
```sh
$ cd nginx-X.Y.Z
$ ./configure --add-module=/path/to/nginx-rtmp-module --with-debug ...
```
After compiling set nginx error.log level to *debug* in nginx.conf
```sh
error_log logs/error.log debug;
```
After that you will have _a lot_ of debug info in error.log. Please grep
what your problem relates to (exec, notify etc) and post to [nginx-rtmp google group](https://groups.google.com/group/nginx-rtmp) to help with solving it.

1824
doc/directives.md Normal file

File diff suppressed because it is too large Load diff

69
doc/examples.md Normal file
View file

@ -0,0 +1,69 @@
# Exampled
### Simple Video-on-Demand
```sh
rtmp {
server {
listen 1935;
application vod {
play /var/flvs;
}
}
}
```
### Simple live broadcast service
```sh
rtmp {
server {
listen 1935;
application live {
live on;
}
}
}
```
### Re-translate remote stream
```sh
rtmp {
server {
listen 1935;
application tv {
live on;
pull rtmp://cdn.example.com:443/programs/main pageUrl=http://www.example.com/index.html name=maintv;
}
}
}
```
### Re-translate remote stream with HLS support
```sh
rtmp {
server {
listen 1935;
application tv {
live on;
hls on;
hls_path /tmp/tv2;
hls_fragment 15s;
pull rtmp://tv2.example.com:443/root/new name=tv2;
}
}
}
http {
server {
listen 80;
location /tv2 {
alias /tmp/tv2;
}
}
}
```
### Stream your X screen through RTMP
```sh
$ ffmpeg -f x11grab -follow_mouse centered -r 25 -s cif -i :0.0 -f flv rtmp://localhost/myapp/screen
```

View file

@ -0,0 +1,33 @@
# Exec wrapper in bash
You can write exec wrapper in any language. However you should pay attention to termination process. When publisher closes the stream all executed processed get terminated. If you specify wrapper in ```exec``` directive instead of real ffmpeg then you might end up with your ffmpeg still alive and orphaned until it times out reading input data.
The solution is using signal traps. Here's an example of such wrapper in bash.
```sh
#!/bin/bash
on_die ()
{
# kill all children
pkill -KILL -P $$
}
trap 'on_die' TERM
ffmpeg -i rtmp://localhost/myapp/$1 -c copy -f flv rtmp://localhost/myapp2/$1 &
wait
```
The script registers SIGTERM handler which terminates child ffmpeg. Default signal sent by nginx-rtmp is SIGKILL which cannot be caught. For the above script to behave as expected you need to change exec kill signal with ```exec_kill_signal``` directive. It accept numeric or symbolic signal name (for POSIX.1-1990 signals). Here's example application.
```sh
application myapp {
live on;
exec /var/scripts/exec_wrapper.sh $name;
exec_kill_signal term;
}
application myapp2 {
live on;
}
```

26
doc/faq.md Normal file
View file

@ -0,0 +1,26 @@
# FAQ
#### RTMP stream is not played normally in IE, stream stops after several seconds.
Add this directive to fix the problem
```sh
wait_video on;
```
#### I use `pull` directive to get stream from remote location. That works for RTMP clients but does not work for HLS.
Currently HLS clients do not trigger any events. You cannot pull or exec when HLS client connects to server. However you can use static directives `exec_static`, `pull ... static` to pull the stream always.
#### Seek does not work with flv files recorded by the module.
To make the files seekable add flv metadata with external software like yamdi, flvmeta or ffmpeg.
```sh
exec_record_done yamdi -i $path -o /var/videos/$basename;
```
#### Published stream is missing from stats page after some time and clients fail to connect
Check if you use multiple workers in nginx (`worker_processes`). In such case you have to enable:
```sh
rtmp_auto_push on;
```

View file

@ -0,0 +1,38 @@
# Getting number of subscribers
There's an easy way to display number of clients watching the stream. You need to
Set up statistics page at location `/stat`
```sh
location /stat {
rtmp_stat all;
allow 127.0.0.1;
}
```
Create a simple xsl stylesheet `nclients.xsl` extracting number of stream subscribers
```xsl
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:param name="app"/>
<xsl:param name="name"/>
<xsl:template match="/">
<xsl:value-of select="count(//application[name=$app]/live/stream[name=$name]/client[not(publishing) and flashver])"/>
</xsl:template>
</xsl:stylesheet>
```
Set up a location returning number of suscribers
```sh
location /nclients {
proxy_pass http://127.0.0.1/stat;
xslt_stylesheet /www/nclients.xsl app='$arg_app' name='$arg_name';
add_header Refresh "3; $request_uri";
}
```
Use HTTP request `http://myserver.com/nclients?app=myapp&name=mystream` to get the number of stream subscribers. This number will be automatically refreshed every 3 seconds when opened in browser or iframe.

188
doc/getting_started.md Normal file
View file

@ -0,0 +1,188 @@
# Getting started with nginx rtmp
## Download, build and install
CD to build directory (home)
```sh
$ cd /usr/build
```
Download & unpack latest nginx-rtmp (you can also use http)
```sh
$ git clone git://github.com/sergey-dryabzhinsky/nginx-rtmp-module
```
Download & unpack nginx (you can also use svn)
```sh
$ wget http://nginx.org/download/nginx-1.2.4.tar.gz
$ tar xzf nginx-1.2.4.tar.gz
$ cd nginx-1.2.4
```
Build nginx with nginx-rtmp
```sh
$ ./configure --add-module=/usr/build/nginx-rtmp-module
$ make
$ make install
```
For nginx 1.3.4-1.5.0 more options are needed
```sh
$ ./configure --add-module=/usr/build/nginx-rtmp-module --with-http_ssl_module
$ make
$ make install
```
## Set up live streaming
To set up RTMP support you need to add `rtmp{}` section to `nginx.conf` (can be found in PREFIX/conf/nginx.conf). Stock `nginx.conf` contains only `http{}` section.
Use this `nginx.conf` instead of stock config:
```sh
#user nobody;
worker_processes 1;
error_log logs/error.log debug;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 8080;
server_name localhost;
# sample handlers
#location /on_play {
# if ($arg_pageUrl ~* localhost) {
# return 201;
# }
# return 202;
#}
#location /on_publish {
# return 201;
#}
#location /vod {
# alias /var/myvideos;
#}
# rtmp stat
location /stat {
rtmp_stat all;
rtmp_stat_stylesheet stat.xsl;
}
location /stat.xsl {
# you can move stat.xsl to a different location
root /usr/build/nginx-rtmp-module;
}
# rtmp control
location /control {
rtmp_control all;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
rtmp {
server {
listen 1935;
ping 30s;
notify_method get;
application myapp {
live on;
# sample play/publish handlers
#on_play http://localhost:8080/on_play;
#on_publish http://localhost:8080/on_publish;
# sample recorder
#recorder rec1 {
# record all;
# record_interval 30s;
# record_path /tmp;
# record_unique on;
#}
# sample HLS
#hls on;
#hls_path /tmp/hls;
#hls_sync 100ms;
}
# Video on demand
#application vod {
# play /var/Videos;
#}
# Video on demand over HTTP
#application vod_http {
# play http://localhost:8080/vod/;
#}
}
}
```
## Statistics
Navigate your browser to `http://localhost:8080/stat` to see current
streaming statistics, connected clients, bandwidth etc.
## Publishing with ffmpeg
The easiest way to publish live video stream is using ffmpeg (or avconv).
It's already installed on most systems and easy to install on others.
RTMP supports only a limited number of codecs. The most popular RTMP video
codecs are H264, Sorenson-H263 (aka flv) and audio codecs AAC, MP3,
Nellymoser, Speex. If your video is encoded with these codecs
(the most common pair is H264/AAC) then you do not need any conversion.
Otherwise you need to convert video to one of supported codecs.
We'll stream test file `/var/videos/test.mp4` to server with ffmpeg.
Streaming without conversion (given `test.mp4` codecs are compatible with RTMP)
```sh
$ ffmpeg -re -i /var/Videos/test.mp4 -c copy -f flv rtmp://localhost/myapp/mystream
```
Streaming and encoding audio (AAC) and video (H264), need `libx264` and `libfaac`
```sh
$ ffmpeg -re -i /var/Videos/test.mp4 -c:v libx264 -c:a libfaac -ar 44100 -ac 1 -f flv rtmp://localhost/myapp/mystream
```
Streaming and encoding audio (MP3) and video (H264), need `libx264` and `libmp3lame`
```sh
$ ffmpeg -re -i /var/Videos/test.mp4 -c:v libx264 -c:a libmp3lame -ar 44100 -ac 1 -f flv rtmp://localhost/myapp/mystream
```
Streaming and encoding audio (Nellymoser) and video (Sorenson H263)
```sh
$ ffmpeg -re -i /var/Videos/test.mp4 -c:v flv -c:a nellymoser -ar 44100 -ac 1 -f flv rtmp://localhost/myapp/mystream
```
## Publishing video from webcam
```sh
$ ffmpeg -f video4linux2 -i /dev/video0 -c:v libx264 -an -f flv rtmp://localhost/myapp/mystream
```
## Playing with ffplay
```sh
$ ffplay rtmp://localhost/myapp/mystream
```
## Publishing and playing with flash
See `test/rtmp-publisher` directory for test flash applets and html.

View file

@ -0,0 +1,24 @@
# Installing in Gentoo
## Download module source code
You have many options:
* Get the zip at https://github.com/arut/nginx-rtmp-module/archive/master.zip
* Or much better, do a git clone (see options in top of https://github.com/arut/nginx-rtmp-module)
* Or get an ebuild from [fem-overlay](http://subversion.fem.tu-ilmenau.de/repository/fem-overlay/trunk/www-servers/nginx/nginx-1.2.5-r1.ebuild). And set the USE flag "nginx_modules_rtmp" or "nginx_modules_rtmp_hls".
## Emerge nginx with nginx-rtmp-module
> NGINX_ADD_MODULES="/path/to/nginx-rtmp-module" emerge -va nginx
Replace `/path/to/` with the actual module's source path.
You can add with this method any number of custom modules.
To make this change permanent see:
http://wiki.gentoo.org/wiki/Knowledge_Base:Overriding_environment_variables_per_package
## Configure nginx
Don't forget to include a rtmp section inside your nginx configuration file located at `/etc/nginx/nginx.conf`.
See:
* [Getting started](getting_started.md) We already have done _Download, build and install_ Gentoo style ;-)
* [More Examples](examples.md)
* [Reference of all directives](directives.md)

View file

@ -0,0 +1,30 @@
# Installing on Ubuntu using PPAs
```sh
$ sudo apt-get install dpkg-dev
$ sudo apt-get source nginx
$ cd /usr/src/nginx
$ sudo git clone https://github.com/arut/nginx-rtmp-module.git
$ cd nginx-[version-number]
$ sudo vi debian/rules
```
Edit the rules and at then end of the add-modules configuration string add
```sh
--add-module=/usr/src/nginx/nginx-rtmp-module \
```
If installing for the first time build nginx dependancies.
```sh
$ sudo apt-get build-dep nginx
$ dpkg-buildpackage -b
```
(wait for a while while it builds... a really long while... like you might want to go grab a meal)
```sh
$ cd .. && sudo dpkg --install nginx-common_1.3.13-1chl1~quantal1_all.deb nginx-full_1.3.13-1chl1~quantal1_amd64.deb
$ sudo service nginx status
$ sudo service nginx start (if nginx isn't running)
```
[Source](http://serverfault.com/questions/227480/installing-optional-nginx-modules-with-apt-get)

114
doc/tutorial.md Normal file
View file

@ -0,0 +1,114 @@
# Tutorial
[This article is not finished yet]
## RTMP
RTMP is a proprietary protocol developed by Adobe (Macromedia) for use
in flash player. Until 2009 it had no public specification.
A number of third-party RTMP-related products started in that period
were based on the results of reverse engineering. In 2009
[RTMP specification](http://www.adobe.com/devnet/rtmp.html) has been
published which made developing such applications easier. However
the spec is not full and misses significant issues concerning streaming H264.
## System requirements
The module has been tested on Linux x86-family platforms.
However it should work on FreeBSD too.
## Licence
The module is distributed under BSD license.
## Building NGINX with the module
Building is pretty obvious. Just cd to nginx source directory
and configure nginx this way:
```sh
$ ./configure --add-module=/path/to/nginx-rtmp-module
```
Then `make` and `make install`.
## Configuration
## Simple live application
Simple live application configuration:
```sh
application live {
live on;
}
```
You can add access list control:
```sh
application live {
live on;
allow publish 127.0.0.1;
deny publish all;
allow play all;
}
```
And you can add record support for live streams:
```sh
application live {
live on;
allow publish 127.0.0.1;
deny publish all;
allow play all;
record all;
record_path /path/to/record/dir;
record_max_size 100M;
record_unique off;
}
```
## HLS (HTTP Live Streaming)
## Choosing flash player
To watch RTMP stream in browser one should either develop
flash application for that or use one of available flash
players. The most popular players which are proved to have
no problems with the module are:
* [JWPlayer](http://www.longtailvideo.com/)
* [FlowPlayer](http://flowplayer.org/)
* [Strobe Media Playback](http://www.osmf.org/strobe_mediaplayback.html)
* [Clappr](https://github.com/globocom/clappr)
Old versions of JWPlayer (<=4.4) supported capturing video
from webcam. You can find that version in test/ subdirectory.
However audio is not captured by this version of player.
Recent free versions of JWPlayer have no capture capability at
all.
## Transcoding streams
You can use exec directive and ffmpeg for transcoding streams. For example:
```sh
application big {
live on;
exec /usr/bin/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 on demand
## Distributed streaming
## Notifications & access control
## Statistics
## Verifying session
## Utilizing multi-core CPUs

View file

@ -16,6 +16,7 @@ static ngx_rtmp_publish_pt next_publish;
static ngx_rtmp_close_stream_pt next_close_stream;
static ngx_rtmp_stream_begin_pt next_stream_begin;
static ngx_rtmp_stream_eof_pt next_stream_eof;
static ngx_rtmp_playlist_pt next_playlist;
static char * ngx_rtmp_hls_variant(ngx_conf_t *cf, ngx_command_t *cmd,
@ -29,8 +30,12 @@ static ngx_int_t ngx_rtmp_hls_ensure_directory(ngx_rtmp_session_t *s,
ngx_str_t *path);
#define NGX_RTMP_HLS_BUFSIZE (1024*1024)
#define NGX_RTMP_HLS_DIR_ACCESS 0744
/* Big buffer for 8k (QHD) cameras */
#ifndef NGX_RTMP_HLS_BUFSIZE
#define NGX_RTMP_HLS_BUFSIZE (16*1024*1024)
#endif
/* Allow access to www-data (web-server) and others too */
#define NGX_RTMP_HLS_DIR_ACCESS 0755
typedef struct {
@ -38,8 +43,8 @@ typedef struct {
uint64_t key_id;
ngx_str_t *datetime;
double duration;
unsigned active:1;
unsigned discont:1; /* before */
u_char active; /* small int, 0/1 */
u_char discont; /* small int, 0/1 */
} ngx_rtmp_hls_frag_t;
@ -50,7 +55,7 @@ typedef struct {
typedef struct {
unsigned opened:1;
u_char opened; /* small int, 0/1 */
ngx_rtmp_mpegts_file_t file;
@ -109,6 +114,7 @@ typedef struct {
ngx_msec_t max_audio_delay;
size_t audio_buffer_size;
ngx_flag_t cleanup;
ngx_uint_t allow_client_cache;
ngx_array_t *variant;
ngx_str_t base_url;
ngx_int_t granularity;
@ -136,6 +142,9 @@ typedef struct {
#define NGX_RTMP_HLS_TYPE_LIVE 1
#define NGX_RTMP_HLS_TYPE_EVENT 2
#define NGX_RTMP_HLS_CACHE_DISABLED 1
#define NGX_RTMP_HLS_CACHE_ENABLED 2
static ngx_conf_enum_t ngx_rtmp_hls_naming_slots[] = {
{ ngx_string("sequential"), NGX_RTMP_HLS_NAMING_SEQUENTIAL },
@ -166,6 +175,11 @@ static ngx_conf_enum_t ngx_rtmp_hls_type_slots[] = {
{ ngx_null_string, 0 }
};
static ngx_conf_enum_t ngx_rtmp_hls_cache[] = {
{ ngx_string("enabled"), NGX_RTMP_HLS_CACHE_ENABLED },
{ ngx_string("disabled"), NGX_RTMP_HLS_CACHE_DISABLED },
{ ngx_null_string, 0 }
};
static ngx_command_t ngx_rtmp_hls_commands[] = {
@ -281,6 +295,13 @@ static ngx_command_t ngx_rtmp_hls_commands[] = {
offsetof(ngx_rtmp_hls_app_conf_t, cleanup),
NULL },
{ ngx_string("hls_allow_client_cache"),
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_hls_app_conf_t, allow_client_cache),
&ngx_rtmp_hls_cache },
{ ngx_string("hls_variant"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_1MORE,
ngx_rtmp_hls_variant,
@ -422,6 +443,8 @@ ngx_rtmp_hls_write_variant_playlist(ngx_rtmp_session_t *s)
ngx_rtmp_hls_variant_t *var;
ngx_rtmp_hls_app_conf_t *hacf;
ngx_rtmp_playlist_t v;
hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_hls_module);
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module);
@ -454,7 +477,7 @@ ngx_rtmp_hls_write_variant_playlist(ngx_rtmp_session_t *s)
p = buffer;
last = buffer + sizeof(buffer);
p = ngx_slprintf(p, last, "#EXT-X-STREAM-INF:PROGRAM-ID=1");
p = ngx_slprintf(p, last, "#EXT-X-STREAM-INF:PROGRAM-ID=1,CLOSED-CAPTIONS=NONE");
arg = var->args.elts;
for (k = 0; k < var->args.nelts; k++, arg++) {
@ -497,12 +520,16 @@ ngx_rtmp_hls_write_variant_playlist(ngx_rtmp_session_t *s)
return NGX_ERROR;
}
return NGX_OK;
ngx_memzero(&v, sizeof(v));
ngx_str_set(&(v.module), "hls");
v.playlist.data = ctx->playlist.data;
v.playlist.len = ctx->playlist.len;
return next_playlist(s, &v);
}
static ngx_int_t
ngx_rtmp_hls_write_playlist(ngx_rtmp_session_t *s)
ngx_rtmp_hls_write_playlist(ngx_rtmp_session_t *s, int final)
{
static u_char buffer[1024];
ngx_fd_t fd;
@ -511,11 +538,17 @@ ngx_rtmp_hls_write_playlist(ngx_rtmp_session_t *s)
ssize_t n;
ngx_rtmp_hls_app_conf_t *hacf;
ngx_rtmp_hls_frag_t *f;
ngx_uint_t i, max_frag;
ngx_int_t i, start_i;
ngx_uint_t max_frag;
double fragments_length;
ngx_str_t name_part, key_name_part;
uint64_t prev_key_id;
const char *sep, *key_sep;
ngx_rtmp_playlist_t v;
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"hls: write playlist");
hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_hls_module);
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module);
@ -532,7 +565,32 @@ ngx_rtmp_hls_write_playlist(ngx_rtmp_session_t *s)
max_frag = hacf->fraglen / 1000;
for (i = 0; i < ctx->nfrags; i++) {
/**
* Need to check fragments length sum and playlist max length
* Do backward search
*/
start_i = 0;
fragments_length = 0.;
for (i = ctx->nfrags-1; i >= 0; i--) {
f = ngx_rtmp_hls_get_frag(s, i);
if (f->duration) {
fragments_length += f->duration;
}
/**
* Think that sum of frag length is more than playlist desired length - half minimal frag length
* XXX: sometimes sum of frag lengths are almost playlist length
* but key-frames come at random rate...
*/
if (fragments_length >= hacf->playlen/1000. - max_frag/2) {
start_i = i;
break;
}
}
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"hls: found starting fragment=%i", start_i);
for (i = start_i; i < (ngx_int_t)ctx->nfrags; i++) {
f = ngx_rtmp_hls_get_frag(s, i);
if (f->duration > max_frag) {
max_frag = (ngx_uint_t) (f->duration + .5);
@ -547,12 +605,18 @@ ngx_rtmp_hls_write_playlist(ngx_rtmp_session_t *s)
"#EXT-X-VERSION:3\n"
"#EXT-X-MEDIA-SEQUENCE:%uL\n"
"#EXT-X-TARGETDURATION:%ui\n",
ctx->frag, max_frag);
ctx->frag + start_i, max_frag);
if (hacf->type == NGX_RTMP_HLS_TYPE_EVENT) {
p = ngx_slprintf(p, end, "#EXT-X-PLAYLIST-TYPE:EVENT\n");
}
if (hacf->allow_client_cache == NGX_RTMP_HLS_CACHE_ENABLED) {
p = ngx_slprintf(p, end, "#EXT-X-ALLOW-CACHE:YES\n");
} else if (hacf->allow_client_cache == NGX_RTMP_HLS_CACHE_DISABLED) {
p = ngx_slprintf(p, end, "#EXT-X-ALLOW-CACHE:NO\n");
}
n = ngx_write_fd(fd, buffer, p - buffer);
if (n < 0) {
goto write_err;
@ -573,9 +637,9 @@ ngx_rtmp_hls_write_playlist(ngx_rtmp_session_t *s)
prev_key_id = 0;
for (i = 0; i < ctx->nfrags; i++) {
for (i = start_i; i < (ngx_int_t)ctx->nfrags; i++) {
f = ngx_rtmp_hls_get_frag(s, i);
if (i == 0 && f->datetime && f->datetime->len > 0) {
if ((i == start_i || f->discont) && f->datetime && f->datetime->len > 0) {
p = ngx_snprintf(buffer, sizeof(buffer), "#EXT-X-PROGRAM-DATE-TIME:");
n = ngx_write_fd(fd, buffer, p - buffer);
if (n < 0) {
@ -615,7 +679,7 @@ ngx_rtmp_hls_write_playlist(ngx_rtmp_session_t *s)
ngx_log_debug5(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"hls: fragment frag=%uL, n=%ui/%ui, duration=%.3f, "
"discont=%i",
ctx->frag, i + 1, ctx->nfrags, f->duration, f->discont);
ctx->frag, i + 1, ctx->nfrags, f->duration, (ngx_int_t)f->discont);
n = ngx_write_fd(fd, buffer, p - buffer);
if (n < 0) {
@ -623,6 +687,15 @@ ngx_rtmp_hls_write_playlist(ngx_rtmp_session_t *s)
}
}
if (final)
{
p = ngx_slprintf(p, end, "#EXT-X-ENDLIST\n");
n = ngx_write_fd(fd, buffer, p - buffer);
if (n < 0) {
goto write_err;
}
}
ngx_close_file(fd);
if (ngx_rtmp_hls_rename_file(ctx->playlist_bak.data, ctx->playlist.data)
@ -638,7 +711,11 @@ ngx_rtmp_hls_write_playlist(ngx_rtmp_session_t *s)
return ngx_rtmp_hls_write_variant_playlist(s);
}
return NGX_OK;
ngx_memzero(&v, sizeof(v));
ngx_str_set(&(v.module), "hls");
v.playlist.data = ctx->playlist.data;
v.playlist.len = ctx->playlist.len;
return next_playlist(s, &v);
write_err:
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
@ -877,12 +954,13 @@ ngx_rtmp_hls_get_fragment_datetime(ngx_rtmp_session_t *s, uint64_t ts)
msec += (ts / 90);
ngx_gmtime(msec / 1000, &tm);
datetime->data = (u_char *) ngx_pcalloc(s->connection->pool, ngx_cached_http_log_iso8601.len * sizeof(u_char));
(void) ngx_sprintf(datetime->data, "%4d-%02d-%02dT%02d:%02d:%02d-00:00",
datetime->len = sizeof("1970-01-01T00:00:00.000-00:00") - 1;
datetime->data = (u_char *) ngx_pcalloc(s->connection->pool, datetime->len * sizeof(u_char));
(void) ngx_sprintf(datetime->data, "%4d-%02d-%02dT%02d:%02d:%02d.%03d-00:00",
tm.ngx_tm_year, tm.ngx_tm_mon,
tm.ngx_tm_mday, tm.ngx_tm_hour,
tm.ngx_tm_min, tm.ngx_tm_sec);
datetime->len = ngx_cached_http_log_iso8601.len;
tm.ngx_tm_min, tm.ngx_tm_sec,
msec % 1000);
return datetime;
case NGX_RTMP_HLS_DATETIME_SYSTEM:
@ -898,7 +976,7 @@ ngx_rtmp_hls_get_fragment_datetime(ngx_rtmp_session_t *s, uint64_t ts)
static ngx_int_t
ngx_rtmp_hls_close_fragment(ngx_rtmp_session_t *s)
ngx_rtmp_hls_close_final_fragment(ngx_rtmp_session_t *s, int final)
{
ngx_rtmp_hls_ctx_t *ctx;
@ -916,12 +994,19 @@ ngx_rtmp_hls_close_fragment(ngx_rtmp_session_t *s)
ngx_rtmp_hls_next_frag(s);
ngx_rtmp_hls_write_playlist(s);
ngx_rtmp_hls_write_playlist(s, final);
return NGX_OK;
}
static ngx_int_t
ngx_rtmp_hls_close_fragment(ngx_rtmp_session_t *s)
{
return ngx_rtmp_hls_close_final_fragment(s, 0);
}
static ngx_int_t
ngx_rtmp_hls_open_fragment(ngx_rtmp_session_t *s, uint64_t ts,
ngx_int_t discont)
@ -929,7 +1014,7 @@ ngx_rtmp_hls_open_fragment(ngx_rtmp_session_t *s, uint64_t ts,
uint64_t id;
ngx_fd_t fd;
ngx_str_t *datetime;
ngx_uint_t g;
ngx_uint_t g, mpegts_cc;
ngx_rtmp_hls_ctx_t *ctx;
ngx_rtmp_codec_ctx_t *codec_ctx;
ngx_rtmp_hls_frag_t *f;
@ -1012,12 +1097,15 @@ ngx_rtmp_hls_open_fragment(ngx_rtmp_session_t *s, uint64_t ts,
}
}
ngx_log_debug6(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
// This is continuity counter for TS header
mpegts_cc = (ngx_uint_t)(ctx->nfrags + ctx->frag);
ngx_log_debug7(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"hls: open fragment file='%s', keyfile='%s', "
"frag=%uL, n=%ui, time=%uL, discont=%i",
"frag=%uL, n=%ui, time=%uL, discont=%i, tscc=%ui",
ctx->stream.data,
ctx->keyfile.data ? ctx->keyfile.data : (u_char *) "",
ctx->frag, ctx->nfrags, ts, discont);
ctx->frag, ctx->nfrags, ts, discont, mpegts_cc);
if (hacf->keys &&
ngx_rtmp_mpegts_init_encryption(&ctx->file, ctx->key, 16, ctx->key_id)
@ -1031,7 +1119,7 @@ ngx_rtmp_hls_open_fragment(ngx_rtmp_session_t *s, uint64_t ts,
codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
if (ngx_rtmp_mpegts_open_file(&ctx->file, ctx->stream.data,
s->connection->log, &codec_ctx->audio_codec_id)
s->connection->log, codec_ctx, mpegts_cc)
!= NGX_OK)
{
return NGX_ERROR;
@ -1379,7 +1467,7 @@ ngx_rtmp_hls_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
goto next;
}
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"hls: publish: name='%s' type='%s'",
v->name, v->type);
@ -1579,7 +1667,7 @@ ngx_rtmp_hls_close_stream(ngx_rtmp_session_t *s, ngx_rtmp_close_stream_t *v)
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"hls: close stream");
ngx_rtmp_hls_close_fragment(s);
ngx_rtmp_hls_close_final_fragment(s, 1);
next:
return next_close_stream(s, v);
@ -1658,6 +1746,9 @@ ngx_rtmp_hls_update_fragment(ngx_rtmp_session_t *s, uint64_t ts,
ngx_buf_t *b;
int64_t d;
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"hls: update fragment");
hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_hls_module);
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module);
f = NULL;
@ -2061,6 +2152,7 @@ ngx_rtmp_hls_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"hls: error appending AUD NAL");
}
/* fall through */
case 9:
aud_sent = 1;
break;
@ -2166,6 +2258,9 @@ ngx_rtmp_hls_stream_begin(ngx_rtmp_session_t *s, ngx_rtmp_stream_begin_t *v)
static ngx_int_t
ngx_rtmp_hls_stream_eof(ngx_rtmp_session_t *s, ngx_rtmp_stream_eof_t *v)
{
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"hls: stream eof");
ngx_rtmp_hls_flush_audio(s);
ngx_rtmp_hls_close_fragment(s);
@ -2284,7 +2379,7 @@ ngx_rtmp_hls_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen)
name.data[name.len - 2] == 'u' &&
name.data[name.len - 1] == '8')
{
max_age = playlen / 1000;
max_age = playlen / 500;
} else if (name.len >= 4 && name.data[name.len - 4] == '.' &&
name.data[name.len - 3] == 'k' &&
@ -2319,15 +2414,23 @@ ngx_rtmp_hls_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen)
}
}
#if (nginx_version >= 1011005)
static ngx_msec_t
#else
static time_t
#endif
ngx_rtmp_hls_cleanup(void *data)
{
ngx_rtmp_hls_cleanup_t *cleanup = data;
ngx_rtmp_hls_cleanup_dir(&cleanup->path, cleanup->playlen);
return cleanup->playlen / 500;
// Next callback in half of playlist length time
#if (nginx_version >= 1011005)
return cleanup->playlen / 2;
#else
return cleanup->playlen / 2000;
#endif
}
@ -2383,6 +2486,13 @@ ngx_rtmp_hls_variant(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
}
static ngx_int_t
ngx_rtmp_hls_playlist(ngx_rtmp_session_t *s, ngx_rtmp_playlist_t *v)
{
return next_playlist(s, v);
}
static void *
ngx_rtmp_hls_create_app_conf(ngx_conf_t *cf)
{
@ -2408,6 +2518,7 @@ ngx_rtmp_hls_create_app_conf(ngx_conf_t *cf)
conf->max_audio_delay = NGX_CONF_UNSET_MSEC;
conf->audio_buffer_size = NGX_CONF_UNSET_SIZE;
conf->cleanup = NGX_CONF_UNSET;
conf->allow_client_cache = NGX_CONF_UNSET_UINT;
conf->granularity = NGX_CONF_UNSET;
conf->keys = NGX_CONF_UNSET;
conf->frags_per_key = NGX_CONF_UNSET_UINT;
@ -2426,7 +2537,7 @@ ngx_rtmp_hls_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->hls, prev->hls, 0);
ngx_conf_merge_msec_value(conf->fraglen, prev->fraglen, 5000);
ngx_conf_merge_msec_value(conf->max_fraglen, prev->max_fraglen,
conf->fraglen * 10);
conf->fraglen * 2);
ngx_conf_merge_msec_value(conf->muxdelay, prev->muxdelay, 700);
ngx_conf_merge_msec_value(conf->sync, prev->sync, 2);
ngx_conf_merge_msec_value(conf->playlen, prev->playlen, 30000);
@ -2559,5 +2670,8 @@ ngx_rtmp_hls_postconfiguration(ngx_conf_t *cf)
next_stream_eof = ngx_rtmp_stream_eof;
ngx_rtmp_stream_eof = ngx_rtmp_hls_stream_eof;
next_playlist = ngx_rtmp_playlist;
ngx_rtmp_playlist = ngx_rtmp_hls_playlist;
return NGX_OK;
}

View file

@ -7,19 +7,30 @@
#include <ngx_config.h>
#include <ngx_core.h>
#include "ngx_rtmp_mpegts.h"
#include "ngx_rtmp_mpegts_crc.h"
#include "ngx_rtmp_codec_module.h"
static u_char ngx_rtmp_mpegts_header[] = {
/* TS */
0x47, 0x40, 0x00, 0x10, 0x00,
/* PSI */
0x00, 0xb0, 0x0d, 0x00, 0x01, 0xc1, 0x00, 0x00,
/* https://en.wikipedia.org/wiki/MPEG_transport_stream#Packet */
/* TS Header */
0x47, // Sync byte
0x40, 0x00, // TEI(1) + PUS(1) + TP(1) + PID(13)
0x10, // TSC(2) + AFF(1) + PF(1) + CC(4)
0x00, // adaption_field_length(8)
/* PAT */
0x00, 0x01, 0xf0, 0x01,
/* CRC */
0x2e, 0x70, 0x19, 0x05,
0x00, // table_id(8)
0xb0, 0x0d, // 1011b(4) + section_length(12)
0x00, 0x01, // transport_stream_id(16)
0xc1, 0x00, 0x00, // 11b(2) + VN(5) + CNI(1), section_no(8), last_section_no(8)
/* PAT program loop */
0x00, 0x01, 0xef, 0xff, // program_number(16), reserved(3) + program_map_pid(13)
/* PAT crc (CRC-32-MPEG2) */
0x36, 0x90, 0xe2, 0x3d, // !!! Needs to be recalculated each time any bit in PAT is modified (which we dont do at the moment) !!!
/* stuffing 167 bytes */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
@ -39,16 +50,26 @@ static u_char ngx_rtmp_mpegts_header[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
/* TS */
0x47, 0x50, 0x01, 0x10, 0x00,
/* PSI */
0x02, 0xb0, 0x17, 0x00, 0x01, 0xc1, 0x00, 0x00,
/* TS Header */
0x47, // Sync byte
0x4f, 0xff, // TEI(1) + PUS(1) + TP(1) + PID(13)
0x10, // TSC(2) + AFF(1) + PF(1) + CC(4)
0x00, // adaption_field_length(8)
/* PMT */
0xe1, 0x00,
0xf0, 0x00,
0x1b, 0xe1, 0x00, 0xf0, 0x00, /* h264 */
0x00, 0x00, 0x00, 0x00, 0x00, /* audio placeholder */
0x00, 0x00, 0x00, 0x00, /* audio crc placeholder */
0x02, // table_id(8)
0xb0, 0x12, // 1011b(4) + section_length(12) (section length set below. Ignore this value in here)
0x00, 0x01, // program_number(16)
0xc1, 0x00, 0x00, // 11b(2) + VN(5) + CNI(1), section_no(8), last_section_no(8)
0xe1, 0x00, // reserved(3) + PCR_PID(13)
0xf0, 0x00, // reserved(4) + program_info_length(12)
/* PMT component loop, looped through when writing header */
/* Max size of 14 bytes */
/* Also includes the PMT CRC, calculated dynamically */
0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
/* stuffing 157 bytes */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
@ -69,18 +90,33 @@ static u_char ngx_rtmp_mpegts_header[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
static u_char ngx_rtmp_mpegts_header_h264[] = {
//H.264 Video, PID 0x100
0x1b, // stream_type(8)
0xe1, 0x00, // reserved(3) + elementary_PID(13)
0xf0, 0x00 // reserved(4) + ES_info_length(12)
};
static u_char ngx_rtmp_mpegts_header_mp3[] = {
0x03, 0xe1, 0x01, 0xf0, 0x00, /* mp3 */
/* CRC */
0xd7, 0x86, 0x44, 0x5c, /* crc for mp3 */
//MP3 Audio, PID 0x101
0x03, // stream_type(8)
0xe1, 0x01, // reserved(3) + elementary_PID(13)
0xf0, 0x00 // reserved(4) + ES_info_length(12)
};
static u_char ngx_rtmp_mpegts_header_aac[] = {
0x0f, 0xe1, 0x01, 0xf0, 0x00, /* aac */
/* CRC */
0x2f, 0x44, 0xb9, 0x9b, /* crc for aac */
//ADTS AAC Audio, PID 0x101
0x0f, // stream_type(8)
0xe1, 0x01, // reserved(3) + elementary_PID(13)
0xf0, 0x00 // reserved(4) + ES_info_length(12)
};
#define NGX_RTMP_MPEGTS_PMT_CRC_START_OFFSET 193
#define NGX_RTMP_MPEGTS_PMT_CRC_MIN_LENGTH 12
#define NGX_RTMP_MPEGTS_PMT_SECTION_LENGTH_OFFSET 195
#define NGX_RTMP_MPEGTS_PMT_LOOP_OFFSET 205
#define NGX_RTMP_MPEGTS_PID_SIZE 5
/* 700 ms PCR delay */
#define NGX_RTMP_HLS_DELAY 63000
@ -165,21 +201,53 @@ ngx_rtmp_mpegts_write_file(ngx_rtmp_mpegts_file_t *file, u_char *in,
static ngx_int_t
ngx_rtmp_mpegts_write_header(ngx_rtmp_mpegts_file_t *file, ngx_uint_t *audio_codec_id)
ngx_rtmp_mpegts_write_header(ngx_rtmp_mpegts_file_t *file, ngx_rtmp_codec_ctx_t *codec_ctx, ngx_uint_t mpegts_cc)
{
ngx_int_t next_pid_offset = 0; //Used to track the number of PIDs we have and the offset in 5-byte chunks
if (*audio_codec_id == NGX_RTMP_AUDIO_AAC) {
ngx_memcpy(ngx_rtmp_mpegts_header+210, ngx_rtmp_mpegts_header_aac,
sizeof(ngx_rtmp_mpegts_header_aac));
}
//if (*audio_codec_id == NGX_RTMP_AUDIO_MP3) {
else {
ngx_memcpy(ngx_rtmp_mpegts_header+210, ngx_rtmp_mpegts_header_mp3,
sizeof(ngx_rtmp_mpegts_header_mp3));
//MPEG-TS CC is 4 bits long. Need to truncate it here.
mpegts_cc %= 0x0f;
// And then put it in the headers
ngx_rtmp_mpegts_header[3] = (ngx_rtmp_mpegts_header[3] & 0xf0) + (u_char)mpegts_cc;
ngx_rtmp_mpegts_header[191] = (ngx_rtmp_mpegts_header[191] & 0xf0) + (u_char)mpegts_cc;
//ngx_rtmp_mpegts_header
if (codec_ctx->video_codec_id)
{
//Put h264 PID in the PMT
ngx_memcpy(ngx_rtmp_mpegts_header+NGX_RTMP_MPEGTS_PMT_LOOP_OFFSET+next_pid_offset, ngx_rtmp_mpegts_header_h264, NGX_RTMP_MPEGTS_PID_SIZE);
next_pid_offset += NGX_RTMP_MPEGTS_PID_SIZE;
}
return ngx_rtmp_mpegts_write_file(file, ngx_rtmp_mpegts_header,
sizeof(ngx_rtmp_mpegts_header));
if (codec_ctx->audio_codec_id){
//Put Audio PID in the PMT
if (codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC) {
ngx_memcpy(ngx_rtmp_mpegts_header+NGX_RTMP_MPEGTS_PMT_LOOP_OFFSET+next_pid_offset, ngx_rtmp_mpegts_header_aac, NGX_RTMP_MPEGTS_PID_SIZE);
}
else
{
ngx_memcpy(ngx_rtmp_mpegts_header+NGX_RTMP_MPEGTS_PMT_LOOP_OFFSET+next_pid_offset, ngx_rtmp_mpegts_header_mp3, NGX_RTMP_MPEGTS_PID_SIZE);
}
next_pid_offset += NGX_RTMP_MPEGTS_PID_SIZE;
}
//Set section length of PMT
//PMT is 13 bytes long without any programs in it. Add this in
ngx_rtmp_mpegts_header[NGX_RTMP_MPEGTS_PMT_SECTION_LENGTH_OFFSET] = 13 + next_pid_offset;
//Calculate CRC
ngx_rtmp_mpegts_crc_t crc = ngx_rtmp_mpegts_crc_init();
crc = ngx_rtmp_mpegts_crc_update(crc, ngx_rtmp_mpegts_header+NGX_RTMP_MPEGTS_PMT_CRC_START_OFFSET, NGX_RTMP_MPEGTS_PMT_CRC_MIN_LENGTH+next_pid_offset);
crc = ngx_rtmp_mpegts_crc_finalize(crc);
ngx_rtmp_mpegts_header[NGX_RTMP_MPEGTS_PMT_LOOP_OFFSET+next_pid_offset] = (crc >> 24) & 0xff;
ngx_rtmp_mpegts_header[NGX_RTMP_MPEGTS_PMT_LOOP_OFFSET+next_pid_offset+1] = (crc >> 16) & 0xff;
ngx_rtmp_mpegts_header[NGX_RTMP_MPEGTS_PMT_LOOP_OFFSET+next_pid_offset+2] = (crc >> 8) & 0xff;
ngx_rtmp_mpegts_header[NGX_RTMP_MPEGTS_PMT_LOOP_OFFSET+next_pid_offset+3] = crc & 0xff;
return ngx_rtmp_mpegts_write_file(file, ngx_rtmp_mpegts_header, sizeof(ngx_rtmp_mpegts_header));
}
@ -250,14 +318,12 @@ ngx_rtmp_mpegts_write_frame(ngx_rtmp_mpegts_file_t *file,
if (first) {
if (f->key) {
packet[3] |= 0x20; /* adaptation */
packet[3] |= 0x20; /* adaptation */
*p++ = 7; /* size */
*p++ = 0x50; /* random access + PCR */
*p++ = 7; /* size */
*p++ = 0x50; /* random access + PCR */
p = ngx_rtmp_mpegts_write_pcr(p, f->dts - NGX_RTMP_HLS_DELAY);
}
p = ngx_rtmp_mpegts_write_pcr(p, f->dts - NGX_RTMP_HLS_DELAY);
/* PES header */
@ -371,7 +437,7 @@ ngx_rtmp_mpegts_init_encryption(ngx_rtmp_mpegts_file_t *file,
ngx_int_t
ngx_rtmp_mpegts_open_file(ngx_rtmp_mpegts_file_t *file, u_char *path,
ngx_log_t *log, ngx_uint_t *audio_codec_id)
ngx_log_t *log, ngx_rtmp_codec_ctx_t *codec_ctx, ngx_uint_t mpegts_cc)
{
file->log = log;
@ -386,7 +452,7 @@ ngx_rtmp_mpegts_open_file(ngx_rtmp_mpegts_file_t *file, u_char *path,
file->size = 0;
if (ngx_rtmp_mpegts_write_header(file, audio_codec_id) != NGX_OK) {
if (ngx_rtmp_mpegts_write_header(file, codec_ctx, mpegts_cc) != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, log, ngx_errno,
"hls: error writing fragment header");
ngx_close_file(file->fd);

View file

@ -12,6 +12,8 @@
#include <ngx_core.h>
#include <openssl/aes.h>
#include <ngx_rtmp_codec_module.h>
typedef struct {
ngx_fd_t fd;
@ -37,7 +39,7 @@ typedef struct {
ngx_int_t ngx_rtmp_mpegts_init_encryption(ngx_rtmp_mpegts_file_t *file,
u_char *key, size_t key_len, uint64_t iv);
ngx_int_t ngx_rtmp_mpegts_open_file(ngx_rtmp_mpegts_file_t *file, u_char *path,
ngx_log_t *log, ngx_uint_t *audio_codec_id);
ngx_log_t *log, ngx_rtmp_codec_ctx_t *codec_ctx, ngx_uint_t mpegts_cc);
ngx_int_t ngx_rtmp_mpegts_close_file(ngx_rtmp_mpegts_file_t *file);
ngx_int_t ngx_rtmp_mpegts_write_frame(ngx_rtmp_mpegts_file_t *file,
ngx_rtmp_mpegts_frame_t *f, ngx_buf_t *b);

80
hls/ngx_rtmp_mpegts_crc.c Normal file
View file

@ -0,0 +1,80 @@
/**
* \file crc.c
* Functions and types for CRC checks.
*
* Generated on Thu May 5 15:32:31 2016,
* by pycrc v0.9, https://pycrc.org
* using the configuration:
* Width = 32
* Poly = 0x04c11db7
* Xor_In = 0xffffffff
* ReflectIn = False
* Xor_Out = 0x00000000
* ReflectOut = False
* Algorithm = table-driven
*****************************************************************************/
#include "ngx_rtmp_mpegts_crc.h" /* include the header file generated with pycrc */
#include <stdlib.h>
#include <stdint.h>
/**
* Static table used for the table_driven implementation.
*****************************************************************************/
static const ngx_rtmp_mpegts_crc_t ngx_rtmp_mpegts_crc_table[256] = {
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};
/**
* Update the crc value with new data.
*
* \param crc The current crc value.
* \param data Pointer to a buffer of \a data_len bytes.
* \param data_len Number of bytes in the \a data buffer.
* \return The updated crc value.
*****************************************************************************/
ngx_rtmp_mpegts_crc_t ngx_rtmp_mpegts_crc_update(ngx_rtmp_mpegts_crc_t crc, const void *data, size_t data_len)
{
const unsigned char *d = (const unsigned char *)data;
unsigned int tbl_idx;
while (data_len--) {
tbl_idx = ((crc >> 24) ^ *d) & 0xff;
crc = (ngx_rtmp_mpegts_crc_table[tbl_idx] ^ (crc << 8)) & 0xffffffff;
d++;
}
return crc & 0xffffffff;
}

83
hls/ngx_rtmp_mpegts_crc.h Normal file
View file

@ -0,0 +1,83 @@
/**
* \file crc.h
* Functions and types for CRC checks.
*
* Generated on Thu May 5 15:32:22 2016,
* by pycrc v0.9, https://pycrc.org
* using the configuration:
* Width = 32
* Poly = 0x04c11db7
* Xor_In = 0xffffffff
* ReflectIn = False
* Xor_Out = 0x00000000
* ReflectOut = False
* Algorithm = table-driven
*****************************************************************************/
#ifndef _NGX_RTMP_MPEGTS_CRC_H_INCLUDED_
#define _NGX_RTMP_MPEGTS_CRC_H_INCLUDED_
#include <stdlib.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* The definition of the used algorithm.
*
* This is not used anywhere in the generated code, but it may be used by the
* application code to call algoritm-specific code, is desired.
*****************************************************************************/
#define CRC_ALGO_TABLE_DRIVEN 1
/**
* The type of the CRC values.
*
* This type must be big enough to contain at least 32 bits.
*****************************************************************************/
typedef uint_fast32_t ngx_rtmp_mpegts_crc_t;
/**
* Calculate the initial crc value.
*
* \return The initial crc value.
*****************************************************************************/
static inline ngx_rtmp_mpegts_crc_t ngx_rtmp_mpegts_crc_init(void)
{
return 0xffffffff;
}
/**
* Update the crc value with new data.
*
* \param crc The current crc value.
* \param data Pointer to a buffer of \a data_len bytes.
* \param data_len Number of bytes in the \a data buffer.
* \return The updated crc value.
*****************************************************************************/
ngx_rtmp_mpegts_crc_t ngx_rtmp_mpegts_crc_update(ngx_rtmp_mpegts_crc_t crc, const void *data, size_t data_len);
/**
* Calculate the final crc value.
*
* \param crc The current crc value.
* \return The final crc value.
*****************************************************************************/
static inline ngx_rtmp_mpegts_crc_t ngx_rtmp_mpegts_crc_finalize(ngx_rtmp_mpegts_crc_t crc)
{
return crc ^ 0x00000000;
}
#ifdef __cplusplus
} /* closing brace for extern "C" */
#endif
#endif /* _NGX_RTMP_MPEGTS_CRC_H_INCLUDED_ */

View file

@ -87,6 +87,7 @@ ngx_rtmp_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
ngx_uint_t i, m, mi, s;
ngx_conf_t pcf;
ngx_array_t ports;
ngx_module_t **modules;
ngx_rtmp_listen_t *listen;
ngx_rtmp_module_t *module;
ngx_rtmp_conf_ctx_t *ctx;
@ -101,14 +102,18 @@ ngx_rtmp_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
*(ngx_rtmp_conf_ctx_t **) conf = ctx;
/* count the number of the rtmp modules and set up their indices */
#if defined(nginx_version) && nginx_version >= 1009011
modules = cf->cycle->modules;
#else
modules = ngx_modules;
#endif
ngx_rtmp_max_module = 0;
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_RTMP_MODULE) {
for (m = 0; modules[m]; m++) {
if (modules[m]->type != NGX_RTMP_MODULE) {
continue;
}
ngx_modules[m]->ctx_index = ngx_rtmp_max_module++;
modules[m]->ctx_index = ngx_rtmp_max_module++;
}
@ -148,13 +153,13 @@ ngx_rtmp_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
* of the all rtmp modules
*/
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_RTMP_MODULE) {
for (m = 0; modules[m]; m++) {
if (modules[m]->type != NGX_RTMP_MODULE) {
continue;
}
module = ngx_modules[m]->ctx;
mi = ngx_modules[m]->ctx_index;
module = modules[m]->ctx;
mi = modules[m]->ctx_index;
if (module->create_main_conf) {
ctx->main_conf[mi] = module->create_main_conf(cf);
@ -181,12 +186,12 @@ ngx_rtmp_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
pcf = *cf;
cf->ctx = ctx;
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_RTMP_MODULE) {
for (m = 0; modules[m]; m++) {
if (modules[m]->type != NGX_RTMP_MODULE) {
continue;
}
module = ngx_modules[m]->ctx;
module = modules[m]->ctx;
if (module->preconfiguration) {
if (module->preconfiguration(cf) != NGX_OK) {
@ -212,13 +217,13 @@ ngx_rtmp_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
cmcf = ctx->main_conf[ngx_rtmp_core_module.ctx_index];
cscfp = cmcf->servers.elts;
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_RTMP_MODULE) {
for (m = 0; modules[m]; m++) {
if (modules[m]->type != NGX_RTMP_MODULE) {
continue;
}
module = ngx_modules[m]->ctx;
mi = ngx_modules[m]->ctx_index;
module = modules[m]->ctx;
mi = modules[m]->ctx_index;
/* init rtmp{} main_conf's */
@ -283,12 +288,12 @@ ngx_rtmp_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
return NGX_CONF_ERROR;
}
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_RTMP_MODULE) {
for (m = 0; modules[m]; m++) {
if (modules[m]->type != NGX_RTMP_MODULE) {
continue;
}
module = ngx_modules[m]->ctx;
module = modules[m]->ctx;
if (module->postconfiguration) {
if (module->postconfiguration(cf) != NGX_OK) {

View file

@ -409,9 +409,9 @@ ngx_rtmp_access_rule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
if (!all) {
break;
}
/* "all" passes through */
#endif
/* fall through */
default: /* AF_INET */
@ -449,10 +449,17 @@ next:
static ngx_int_t
ngx_rtmp_access_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
{
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"access: ngx_rtmp_access_play");
if (ngx_rtmp_access(s, NGX_RTMP_ACCESS_PLAY) != NGX_OK) {
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"access: ngx_rtmp_access_play: error");
return NGX_ERROR;
}
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"access: ngx_rtmp_access_play: next");
return next_play(s, v);
}

View file

@ -328,9 +328,10 @@ ngx_rtmp_amf_read(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
} else {
switch (ngx_rtmp_amf_get(ctx, &type8, 1)) {
case NGX_DONE:
if (elts->type & NGX_RTMP_AMF_OPTIONAL) {
if (elts && elts->type & NGX_RTMP_AMF_OPTIONAL) {
return NGX_OK;
}
/* fall through */
case NGX_ERROR:
return NGX_ERROR;
}
@ -398,6 +399,7 @@ ngx_rtmp_amf_read(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
if (ngx_rtmp_amf_get(ctx, &max_index, 4) != NGX_OK) {
return NGX_ERROR;
}
/* fall through */
case NGX_RTMP_AMF_OBJECT:
if (ngx_rtmp_amf_read_object(ctx, data,
@ -592,6 +594,7 @@ ngx_rtmp_amf_write(ngx_rtmp_amf_ctx_t *ctx,
if (ngx_rtmp_amf_put(ctx, &max_index, 4) != NGX_OK) {
return NGX_ERROR;
}
/* fall through */
case NGX_RTMP_AMF_OBJECT:
type8 = NGX_RTMP_AMF_END;

View file

@ -93,6 +93,34 @@ ngx_module_t ngx_rtmp_auto_push_module = {
};
static ngx_rtmp_module_t ngx_rtmp_auto_push_index_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create app configuration */
NULL /* merge app configuration */
};
ngx_module_t ngx_rtmp_auto_push_index_module = {
NGX_MODULE_V1,
&ngx_rtmp_auto_push_index_module_ctx, /* module context */
NULL, /* module directives */
NGX_RTMP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
#define NGX_RTMP_AUTO_PUSH_SOCKNAME "nginx-rtmp"
@ -324,7 +352,7 @@ ngx_rtmp_auto_push_reconnect(ngx_event_t *ev)
apcf = (ngx_rtmp_auto_push_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_rtmp_auto_push_module);
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_auto_push_module);
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_auto_push_index_module);
if (ctx == NULL) {
return;
}
@ -461,14 +489,14 @@ ngx_rtmp_auto_push_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
goto next;
}
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_auto_push_module);
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_auto_push_index_module);
if (ctx == NULL) {
ctx = ngx_palloc(s->connection->pool,
sizeof(ngx_rtmp_auto_push_ctx_t));
if (ctx == NULL) {
goto next;
}
ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_auto_push_module);
ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_auto_push_index_module);
}
ngx_memzero(ctx, sizeof(*ctx));
@ -508,7 +536,7 @@ ngx_rtmp_auto_push_delete_stream(ngx_rtmp_session_t *s,
goto next;
}
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_auto_push_module);
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_auto_push_index_module);
if (ctx) {
if (ctx->push_evt.timer_set) {
ngx_del_timer(&ctx->push_evt);
@ -532,7 +560,7 @@ ngx_rtmp_auto_push_delete_stream(ngx_rtmp_session_t *s,
slot, &rctx->app, &rctx->name);
pctx = ngx_rtmp_get_module_ctx(rctx->publish->session,
ngx_rtmp_auto_push_module);
ngx_rtmp_auto_push_index_module);
if (pctx == NULL) {
goto next;
}

View file

@ -44,6 +44,7 @@ static ngx_int_t ngx_rtmp_cmd_recorded(ngx_rtmp_session_t *s,
static ngx_int_t ngx_rtmp_cmd_set_buflen(ngx_rtmp_session_t *s,
ngx_rtmp_set_buflen_t *v);
static ngx_int_t ngx_rtmp_cmd_playlist(ngx_rtmp_session_t *s, ngx_rtmp_playlist_t *v);
ngx_rtmp_connect_pt ngx_rtmp_connect;
ngx_rtmp_disconnect_pt ngx_rtmp_disconnect;
@ -62,6 +63,7 @@ ngx_rtmp_stream_dry_pt ngx_rtmp_stream_dry;
ngx_rtmp_recorded_pt ngx_rtmp_recorded;
ngx_rtmp_set_buflen_pt ngx_rtmp_set_buflen;
ngx_rtmp_playlist_pt ngx_rtmp_playlist;
static ngx_int_t ngx_rtmp_cmd_postconfiguration(ngx_conf_t *cf);
@ -574,6 +576,8 @@ ngx_rtmp_cmd_play_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
static ngx_int_t
ngx_rtmp_cmd_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
{
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"cmd: ngx_rtmp_cmd_play");
return NGX_OK;
}
@ -786,6 +790,14 @@ ngx_rtmp_cmd_set_buflen(ngx_rtmp_session_t *s, ngx_rtmp_set_buflen_t *v)
}
static ngx_int_t
ngx_rtmp_cmd_playlist(ngx_rtmp_session_t *s, ngx_rtmp_playlist_t *v)
{
return NGX_OK;
}
static ngx_rtmp_amf_handler_t ngx_rtmp_cmd_map[] = {
{ ngx_string("connect"), ngx_rtmp_cmd_connect_init },
{ ngx_string("createStream"), ngx_rtmp_cmd_create_stream_init },
@ -852,5 +864,7 @@ ngx_rtmp_cmd_postconfiguration(ngx_conf_t *cf)
ngx_rtmp_recorded = ngx_rtmp_cmd_recorded;
ngx_rtmp_set_buflen = ngx_rtmp_cmd_set_buflen;
ngx_rtmp_playlist = ngx_rtmp_cmd_playlist;
return NGX_OK;
}

View file

@ -14,8 +14,8 @@
#include "ngx_rtmp.h"
#define NGX_RTMP_MAX_NAME 256
#define NGX_RTMP_MAX_URL 256
#define NGX_RTMP_MAX_NAME 2048
#define NGX_RTMP_MAX_URL 4096
#define NGX_RTMP_MAX_ARGS NGX_RTMP_MAX_NAME
@ -25,7 +25,7 @@ typedef struct {
double trans;
u_char app[NGX_RTMP_MAX_NAME];
u_char args[NGX_RTMP_MAX_ARGS];
u_char flashver[32];
u_char flashver[64];
u_char swf_url[NGX_RTMP_MAX_URL];
u_char tc_url[NGX_RTMP_MAX_URL];
double acodecs;
@ -59,6 +59,12 @@ typedef struct {
} ngx_rtmp_publish_t;
typedef struct {
ngx_str_t playlist;
ngx_str_t module;
} ngx_rtmp_playlist_t;
typedef struct {
u_char name[NGX_RTMP_MAX_NAME];
u_char args[NGX_RTMP_MAX_ARGS];
@ -130,6 +136,7 @@ typedef ngx_int_t (*ngx_rtmp_recorded_pt)(ngx_rtmp_session_t *s,
typedef ngx_int_t (*ngx_rtmp_set_buflen_pt)(ngx_rtmp_session_t *s,
ngx_rtmp_set_buflen_t *v);
typedef ngx_int_t (*ngx_rtmp_playlist_pt)(ngx_rtmp_session_t *s, ngx_rtmp_playlist_t *v);
extern ngx_rtmp_connect_pt ngx_rtmp_connect;
extern ngx_rtmp_disconnect_pt ngx_rtmp_disconnect;
@ -147,5 +154,6 @@ extern ngx_rtmp_stream_dry_pt ngx_rtmp_stream_dry;
extern ngx_rtmp_set_buflen_pt ngx_rtmp_set_buflen;
extern ngx_rtmp_recorded_pt ngx_rtmp_recorded;
extern ngx_rtmp_playlist_pt ngx_rtmp_playlist;
#endif /*_NGX_RTMP_CMD_H_INCLUDED_ */

View file

@ -306,7 +306,7 @@ ngx_rtmp_codec_parse_aac_header(ngx_rtmp_session_t *s, ngx_chain_t *in)
ctx->aac_chan_conf = (ngx_uint_t) ngx_rtmp_bit_read(&br, 4);
if (ctx->aac_profile == 5 || ctx->aac_profile == 29) {
if (ctx->aac_profile == 29) {
ctx->aac_ps = 1;
}
@ -343,7 +343,7 @@ ngx_rtmp_codec_parse_aac_header(ngx_rtmp_session_t *s, ngx_chain_t *in)
5 bits: object type
if (object type == 31)
6 bits + 32: object type
var bits: AOT Specific Config
*/
@ -415,7 +415,7 @@ ngx_rtmp_codec_parse_avc_header(ngx_rtmp_session_t *s, ngx_chain_t *in)
{
/* chroma format idc */
cf_idc = (ngx_uint_t) ngx_rtmp_bit_read_golomb(&br);
if (cf_idc == 3) {
/* separate color plane */
@ -454,7 +454,7 @@ ngx_rtmp_codec_parse_avc_header(ngx_rtmp_session_t *s, ngx_chain_t *in)
if (sl_next != 0) {
/* convert to signed: (-1)**k+1 * ceil(k/2) */
sl_udelta = ngx_rtmp_bit_read_golomb(&br);
sl_udelta = (ngx_uint_t)ngx_rtmp_bit_read_golomb(&br);
sl_delta = (sl_udelta + 1) >> 1;
if ((sl_udelta & 1) == 0) {
sl_delta = -sl_delta;
@ -595,6 +595,7 @@ ngx_rtmp_codec_reconstruct_meta(ngx_rtmp_session_t *s)
double duration;
double frame_rate;
double video_data_rate;
double video_keyframe_frequency;
double video_codec_id;
double audio_data_rate;
double audio_codec_id;
@ -606,7 +607,7 @@ ngx_rtmp_codec_reconstruct_meta(ngx_rtmp_session_t *s)
{ NGX_RTMP_AMF_STRING,
ngx_string("Server"),
"NGINX RTMP (github.com/arut/nginx-rtmp-module)", 0 },
"NGINX RTMP (github.com/sergey-dryabzhinsky/nginx-rtmp-module)", 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("width"),
@ -640,6 +641,10 @@ ngx_rtmp_codec_reconstruct_meta(ngx_rtmp_session_t *s)
ngx_string("videodatarate"),
&v.video_data_rate, 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("videokeyframe_frequency"),
&v.video_keyframe_frequency, 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("videocodecid"),
&v.video_codec_id, 0 },
@ -689,6 +694,7 @@ ngx_rtmp_codec_reconstruct_meta(ngx_rtmp_session_t *s)
v.duration = ctx->duration;
v.frame_rate = ctx->frame_rate;
v.video_data_rate = ctx->video_data_rate;
v.video_keyframe_frequency = ctx->video_keyframe_frequency;
v.video_codec_id = ctx->video_codec_id;
v.audio_data_rate = ctx->audio_data_rate;
v.audio_codec_id = ctx->audio_codec_id;
@ -765,6 +771,7 @@ ngx_rtmp_codec_meta_data(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
double duration;
double frame_rate;
double video_data_rate;
double video_keyframe_frequency;
double video_codec_id_n;
u_char video_codec_id_s[32];
double audio_data_rate;
@ -822,6 +829,10 @@ ngx_rtmp_codec_meta_data(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_string("videodatarate"),
&v.video_data_rate, 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("videokeyframe_frequency"),
&v.video_keyframe_frequency, 0 },
{ NGX_RTMP_AMF_VARIANT,
ngx_string("videocodecid"),
in_video_codec_id, sizeof(in_video_codec_id) },
@ -845,7 +856,8 @@ ngx_rtmp_codec_meta_data(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
static ngx_rtmp_amf_elt_t in_elts[] = {
{ NGX_RTMP_AMF_STRING,
/* That string is passed by FFmpeg and possibly others (librtmp). It's skipped after at #880 */
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
NULL, 0 },
@ -870,13 +882,14 @@ ngx_rtmp_codec_meta_data(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
v.duration = -1;
v.frame_rate = -1;
v.video_data_rate = -1;
v.video_keyframe_frequency = -1;
v.video_codec_id_n = -1;
v.audio_data_rate = -1;
v.audio_codec_id_n = -1;
v.profile[0] = '\0';
v.level[0] = '\0';
/* FFmpeg sends a string in front of actal metadata; ignore it */
/* FFmpeg sends a string in front of actual metadata; ignore it */
skip = !(in->buf->last > in->buf->pos
&& *in->buf->pos == NGX_RTMP_AMF_STRING);
if (ngx_rtmp_receive_amf(s, in, in_elts + skip,
@ -891,15 +904,16 @@ ngx_rtmp_codec_meta_data(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
if (v.height != -1) ctx->height = (ngx_uint_t) v.height;
if (v.duration != -1) ctx->duration = (double) v.duration;
if (v.frame_rate != -1) ctx->frame_rate = (double) v.frame_rate;
if (v.video_data_rate != -1) ctx->video_data_rate = (ngx_uint_t) v.video_data_rate;
if (v.video_data_rate != -1) ctx->video_data_rate = v.video_data_rate;
if (v.video_codec_id_n != -1) ctx->video_codec_id = (ngx_uint_t) v.video_codec_id_n;
if (v.audio_data_rate != -1) ctx->audio_data_rate = (ngx_uint_t) v.audio_data_rate;
if (v.audio_data_rate != -1) ctx->audio_data_rate = v.audio_data_rate;
if (v.video_keyframe_frequency != -1) ctx->video_keyframe_frequency = v.video_keyframe_frequency;
if (v.audio_codec_id_n != -1) ctx->audio_codec_id = (v.audio_codec_id_n == 0
? NGX_RTMP_AUDIO_UNCOMPRESSED : (ngx_uint_t) v.audio_codec_id_n);
if (v.profile[0] != '\0') ngx_memcpy(ctx->profile, v.profile, sizeof(v.profile));
if (v.level[0] != '\0') ngx_memcpy(ctx->level, v.level, sizeof(v.level));
ngx_log_debug8(NGX_LOG_DEBUG, s->connection->log, 0,
ngx_log_debug8(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"codec: data frame: "
"width=%ui height=%ui duration=%.3f frame_rate=%.3f "
"video=%s (%ui) audio=%s (%ui)",
@ -909,6 +923,12 @@ ngx_rtmp_codec_meta_data(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_rtmp_get_audio_codec_name(ctx->audio_codec_id),
ctx->audio_codec_id);
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"codec: data frame: "
"video_rate=%.3f audio_rate=%.3f ",
ctx->video_data_rate, ctx->audio_data_rate
);
switch (cacf->meta) {
case NGX_RTMP_CODEC_META_ON:
return ngx_rtmp_codec_reconstruct_meta(s);
@ -975,7 +995,7 @@ ngx_rtmp_codec_postconfiguration(ngx_conf_t *cf)
}
ngx_str_set(&ch->name, "@setDataFrame");
ch->handler = ngx_rtmp_codec_meta_data;
// some encoders send setDataFrame instead of @setDataFrame
ch = ngx_array_push(&cmcf->amf);
if (ch == NULL) {
@ -983,7 +1003,7 @@ ngx_rtmp_codec_postconfiguration(ngx_conf_t *cf)
}
ngx_str_set(&ch->name, "setDataFrame");
ch->handler = ngx_rtmp_codec_meta_data;
ch = ngx_array_push(&cmcf->amf);
if (ch == NULL) {
return NGX_ERROR;

View file

@ -54,9 +54,10 @@ typedef struct {
ngx_uint_t height;
double duration;
double frame_rate;
ngx_uint_t video_data_rate;
double video_data_rate;
double video_keyframe_frequency;
ngx_uint_t video_codec_id;
ngx_uint_t audio_data_rate;
double audio_data_rate;
ngx_uint_t audio_codec_id;
ngx_uint_t aac_profile;
ngx_uint_t aac_chan_conf;

View file

@ -45,7 +45,7 @@ static ngx_command_t ngx_rtmp_core_commands[] = {
NULL },
{ ngx_string("listen"),
NGX_RTMP_SRV_CONF|NGX_CONF_TAKE12,
NGX_RTMP_SRV_CONF|NGX_CONF_1MORE,
ngx_rtmp_core_listen,
NGX_RTMP_SRV_CONF_OFFSET,
0,
@ -332,6 +332,7 @@ ngx_rtmp_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
void *mconf;
ngx_uint_t m;
ngx_conf_t pcf;
ngx_module_t **modules;
ngx_rtmp_module_t *module;
ngx_rtmp_conf_ctx_t *ctx, *rtmp_ctx;
ngx_rtmp_core_srv_conf_t *cscf, **cscfp;
@ -357,12 +358,17 @@ ngx_rtmp_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
return NGX_CONF_ERROR;
}
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_RTMP_MODULE) {
#if defined(nginx_version) && nginx_version >= 1009011
modules = cf->cycle->modules;
#else
modules = ngx_modules;
#endif
for (m = 0; modules[m]; m++) {
if (modules[m]->type != NGX_RTMP_MODULE) {
continue;
}
module = ngx_modules[m]->ctx;
module = modules[m]->ctx;
if (module->create_srv_conf) {
mconf = module->create_srv_conf(cf);
@ -370,7 +376,7 @@ ngx_rtmp_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
return NGX_CONF_ERROR;
}
ctx->srv_conf[ngx_modules[m]->ctx_index] = mconf;
ctx->srv_conf[modules[m]->ctx_index] = mconf;
}
if (module->create_app_conf) {
@ -379,7 +385,7 @@ ngx_rtmp_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
return NGX_CONF_ERROR;
}
ctx->app_conf[ngx_modules[m]->ctx_index] = mconf;
ctx->app_conf[modules[m]->ctx_index] = mconf;
}
}
@ -419,6 +425,7 @@ ngx_rtmp_core_application(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
ngx_int_t i;
ngx_str_t *value;
ngx_conf_t save;
ngx_module_t **modules;
ngx_rtmp_module_t *module;
ngx_rtmp_conf_ctx_t *ctx, *pctx;
ngx_rtmp_core_srv_conf_t *cscf;
@ -438,17 +445,21 @@ ngx_rtmp_core_application(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
return NGX_CONF_ERROR;
}
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_RTMP_MODULE) {
#if defined(nginx_version) && nginx_version >= 1009011
modules = cf->cycle->modules;
#else
modules = ngx_modules;
#endif
for (i = 0; modules[i]; i++) {
if (modules[i]->type != NGX_RTMP_MODULE) {
continue;
}
module = ngx_modules[i]->ctx;
module = modules[i]->ctx;
if (module->create_app_conf) {
ctx->app_conf[ngx_modules[i]->ctx_index] =
module->create_app_conf(cf);
if (ctx->app_conf[ngx_modules[i]->ctx_index] == NULL) {
ctx->app_conf[modules[i]->ctx_index] = module->create_app_conf(cf);
if (ctx->app_conf[modules[i]->ctx_index] == NULL) {
return NGX_CONF_ERROR;
}
}
@ -488,7 +499,7 @@ ngx_rtmp_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
in_port_t port;
ngx_str_t *value;
ngx_url_t u;
ngx_uint_t i, m;
ngx_uint_t i;
struct sockaddr *sa;
ngx_rtmp_listen_t *ls;
struct sockaddr_in *sin;
@ -545,7 +556,11 @@ ngx_rtmp_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
break;
}
#if (nginx_version >= 1011000)
if (ngx_memcmp(ls[i].sockaddr + off, (u_char *) &u.sockaddr + off, len) != 0) {
#else
if (ngx_memcmp(ls[i].sockaddr + off, u.sockaddr + off, len) != 0) {
#endif
continue;
}
@ -565,18 +580,16 @@ ngx_rtmp_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
ngx_memzero(ls, sizeof(ngx_rtmp_listen_t));
#if (nginx_version >= 1011000)
ngx_memcpy(ls->sockaddr, (u_char *) &u.sockaddr, u.socklen);
#else
ngx_memcpy(ls->sockaddr, u.sockaddr, u.socklen);
#endif
ls->socklen = u.socklen;
ls->wildcard = u.wildcard;
ls->ctx = cf->ctx;
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_RTMP_MODULE) {
continue;
}
}
for (i = 2; i < cf->args->nelts; i++) {
if (ngx_strcmp(value[i].data, "bind") == 0) {
@ -586,7 +599,6 @@ ngx_rtmp_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
if (ngx_strncmp(value[i].data, "ipv6only=o", 10) == 0) {
#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
struct sockaddr *sa;
u_char buf[NGX_SOCKADDR_STRLEN];
sa = (struct sockaddr *) ls->sockaddr;

View file

@ -154,6 +154,7 @@ ngx_rtmp_eval(void *ctx, ngx_str_t *in, ngx_rtmp_eval_t **e, ngx_str_t *out,
name.len = p - name.data;
ngx_rtmp_eval_append_var(ctx, &b, e, &name, log);
/* fall through */
case NORMAL:
switch (c) {
@ -164,7 +165,11 @@ ngx_rtmp_eval(void *ctx, ngx_str_t *in, ngx_rtmp_eval_t **e, ngx_str_t *out,
case '\\':
state = ESCAPE;
continue;
/* fall through */
default:
break;
}
/* fall through */
case ESCAPE:
ngx_rtmp_eval_append(&b, &c, 1, log);

View file

@ -20,6 +20,7 @@
static ngx_rtmp_publish_pt next_publish;
static ngx_rtmp_play_pt next_play;
static ngx_rtmp_close_stream_pt next_close_stream;
static ngx_rtmp_record_started_pt next_record_started;
static ngx_rtmp_record_done_pt next_record_done;
#endif
@ -55,6 +56,7 @@ enum {
NGX_RTMP_EXEC_PUBLISH_DONE,
NGX_RTMP_EXEC_PLAY,
NGX_RTMP_EXEC_PLAY_DONE,
NGX_RTMP_EXEC_RECORD_STARTED,
NGX_RTMP_EXEC_RECORD_DONE,
NGX_RTMP_EXEC_MAX,
@ -208,6 +210,15 @@ static ngx_command_t ngx_rtmp_exec_commands[] = {
NGX_RTMP_EXEC_PLAY_DONE * sizeof(ngx_array_t),
NULL },
{ ngx_string("exec_record_started"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_RTMP_REC_CONF|
NGX_CONF_1MORE,
ngx_rtmp_exec_conf,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_rtmp_exec_app_conf_t, conf) +
NGX_RTMP_EXEC_RECORD_STARTED * sizeof(ngx_array_t),
NULL },
{ ngx_string("exec_record_done"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_RTMP_REC_CONF|
NGX_CONF_1MORE,
@ -1194,6 +1205,9 @@ next:
static ngx_int_t
ngx_rtmp_exec_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
{
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"exec: ngx_rtmp_exec_play");
ngx_rtmp_exec_ctx_t *ctx;
ngx_rtmp_exec_pull_ctx_t *pctx;
ngx_rtmp_exec_app_conf_t *eacf;
@ -1224,6 +1238,8 @@ ngx_rtmp_exec_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
}
next:
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"exec: ngx_rtmp_exec_play: next");
return next_play(s, v);
}
@ -1302,6 +1318,31 @@ next:
}
static ngx_int_t
ngx_rtmp_exec_record_started(ngx_rtmp_session_t *s, ngx_rtmp_record_started_t *v)
{
ngx_rtmp_exec_app_conf_t *eacf;
if (s->auto_pushed) {
goto next;
}
eacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_exec_module);
if (eacf == NULL || !eacf->active) {
goto next;
}
ngx_rtmp_exec_unmanaged(s, &eacf->conf[NGX_RTMP_EXEC_RECORD_STARTED],
"record_started");
ngx_str_null(&v->recorder);
ngx_str_null(&v->path);
next:
return next_record_started(s, v);
}
static ngx_int_t
ngx_rtmp_exec_record_done(ngx_rtmp_session_t *s, ngx_rtmp_record_done_t *v)
{
@ -1359,6 +1400,7 @@ ngx_rtmp_exec_record_done(ngx_rtmp_session_t *s, ngx_rtmp_record_done_t *v)
next:
return next_record_done(s, v);
}
#endif /* NGX_WIN32 */
@ -1598,6 +1640,9 @@ ngx_rtmp_exec_postconfiguration(ngx_conf_t *cf)
next_record_done = ngx_rtmp_record_done;
ngx_rtmp_record_done = ngx_rtmp_exec_record_done;
next_record_started = ngx_rtmp_record_started;
ngx_rtmp_record_started = ngx_rtmp_exec_record_started;
#endif /* NGX_WIN32 */
return NGX_OK;

View file

@ -241,7 +241,9 @@ ngx_rtmp_recv(ngx_event_t *rev)
"reusing formerly read data: %d", old_size);
b->pos = b->start;
b->last = ngx_movemem(b->pos, old_pos, old_size);
size = ngx_min((size_t) (b->end - b->start), old_size);
b->last = ngx_movemem(b->pos, old_pos, size);
if (s->in_chunk_size_changing) {
ngx_rtmp_finalize_set_chunk_size(s);

View file

@ -104,30 +104,37 @@ static ngx_int_t
ngx_rtmp_make_digest(ngx_str_t *key, ngx_buf_t *src,
u_char *skip, u_char *dst, ngx_log_t *log)
{
static HMAC_CTX hmac;
static unsigned hmac_initialized;
static HMAC_CTX *hmac;
unsigned int len;
if (!hmac_initialized) {
HMAC_CTX_init(&hmac);
hmac_initialized = 1;
if (hmac == NULL) {
#if OPENSSL_VERSION_NUMBER < 0x10100000L
static HMAC_CTX shmac;
hmac = &shmac;
HMAC_CTX_init(hmac);
#else
hmac = HMAC_CTX_new();
if (hmac == NULL) {
return NGX_ERROR;
}
#endif
}
HMAC_Init_ex(&hmac, key->data, key->len, EVP_sha256(), NULL);
HMAC_Init_ex(hmac, key->data, key->len, EVP_sha256(), NULL);
if (skip && src->pos <= skip && skip <= src->last) {
if (skip != src->pos) {
HMAC_Update(&hmac, src->pos, skip - src->pos);
HMAC_Update(hmac, src->pos, skip - src->pos);
}
if (src->last != skip + NGX_RTMP_HANDSHAKE_KEYLEN) {
HMAC_Update(&hmac, skip + NGX_RTMP_HANDSHAKE_KEYLEN,
HMAC_Update(hmac, skip + NGX_RTMP_HANDSHAKE_KEYLEN,
src->last - skip - NGX_RTMP_HANDSHAKE_KEYLEN);
}
} else {
HMAC_Update(&hmac, src->pos, src->last - src->pos);
HMAC_Update(hmac, src->pos, src->last - src->pos);
}
HMAC_Final(&hmac, dst, &len);
HMAC_Final(hmac, dst, &len);
return NGX_OK;
}

View file

@ -76,9 +76,9 @@ ngx_rtmp_init_connection(ngx_connection_t *c)
break;
#endif
case AF_UNIX:
unix_socket = 1;
/* fall through */
default: /* AF_INET */
sin = (struct sockaddr_in *) sa;
@ -110,6 +110,7 @@ ngx_rtmp_init_connection(ngx_connection_t *c)
case AF_UNIX:
unix_socket = 1;
/* fall through */
default: /* AF_INET */
addr = port->addrs;

View file

@ -40,16 +40,16 @@ static ngx_command_t ngx_rtmp_live_commands[] = {
{ ngx_string("stream_buckets"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
ngx_conf_set_num_slot,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_rtmp_live_app_conf_t, nbuckets),
NULL },
{ ngx_string("buffer"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
ngx_conf_set_flag_slot,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_rtmp_live_app_conf_t, buflen),
offsetof(ngx_rtmp_live_app_conf_t, buffer),
NULL },
{ ngx_string("sync"),
@ -152,7 +152,7 @@ ngx_rtmp_live_create_app_conf(ngx_conf_t *cf)
lacf->live = NGX_CONF_UNSET;
lacf->nbuckets = NGX_CONF_UNSET;
lacf->buflen = NGX_CONF_UNSET_MSEC;
lacf->buffer = NGX_CONF_UNSET;
lacf->sync = NGX_CONF_UNSET_MSEC;
lacf->idle_timeout = NGX_CONF_UNSET_MSEC;
lacf->interleave = NGX_CONF_UNSET;
@ -174,7 +174,7 @@ ngx_rtmp_live_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->live, prev->live, 0);
ngx_conf_merge_value(conf->nbuckets, prev->nbuckets, 1024);
ngx_conf_merge_msec_value(conf->buflen, prev->buflen, 0);
ngx_conf_merge_value(conf->buffer, prev->buffer, 0);
ngx_conf_merge_msec_value(conf->sync, prev->sync, 300);
ngx_conf_merge_msec_value(conf->idle_timeout, prev->idle_timeout, 0);
ngx_conf_merge_value(conf->interleave, prev->interleave, 0);
@ -553,7 +553,7 @@ ngx_rtmp_live_join(ngx_rtmp_session_t *s, u_char *name, unsigned publisher)
(*stream)->ctx = ctx;
if (lacf->buflen) {
if (lacf->buffer) {
s->out_buffer = 1;
}
@ -1056,9 +1056,11 @@ ngx_rtmp_live_data(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
uint32_t delta;
ngx_rtmp_live_chunk_stream_t *cs;
#ifdef NGX_DEBUG
u_char *msg_type;
msg_type = (u_char *)out_elts[0].data;
#endif
lacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_live_module);
if (lacf == NULL) {
@ -1099,6 +1101,8 @@ ngx_rtmp_live_data(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
data = NULL;
rc = ngx_rtmp_append_amf(s, &data, NULL, out_elts, out_elts_size);
if (rc != NGX_OK) {
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"live: data - can't append amf!");
if (data) {
ngx_rtmp_free_shared_chain(cscf, data);
}
@ -1134,10 +1138,6 @@ ngx_rtmp_live_data(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ss->current_time = cs->timestamp;
}
if (data) {
ngx_rtmp_free_shared_chain(cscf, data);
}
if (rpkt) {
ngx_rtmp_free_shared_chain(cscf, rpkt);
}
@ -1227,7 +1227,7 @@ ngx_rtmp_live_on_fi(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
"onFi", 0 },
"onFI", 0 },
{ NGX_RTMP_AMF_MIXED_ARRAY,
ngx_null_string,
@ -1254,7 +1254,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,
"live: onFi: date='%s', time='%s'",
"live: onFI: date='%s', time='%s'",
v.date, v.time);
out_dt_elts[0].data = v.date;
@ -1426,6 +1426,9 @@ next:
static ngx_int_t
ngx_rtmp_live_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
{
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"live: ngx_rtmp_live_play");
ngx_rtmp_live_app_conf_t *lacf;
ngx_rtmp_live_ctx_t *ctx;
@ -1458,6 +1461,8 @@ ngx_rtmp_live_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
}
next:
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"live: ngx_rtmp_live_play: next");
return next_play(s, v);
}
@ -1508,7 +1513,7 @@ ngx_rtmp_live_postconfiguration(ngx_conf_t *cf)
ch->handler = ngx_rtmp_live_on_cue_point;
ch = ngx_array_push(&cmcf->amf);
ngx_str_set(&ch->name, "onFi");
ngx_str_set(&ch->name, "onFI");
ch->handler = ngx_rtmp_live_on_fi;
ch = ngx_array_push(&cmcf->amf);

View file

@ -72,7 +72,7 @@ typedef struct {
ngx_flag_t publish_notify;
ngx_flag_t play_restart;
ngx_flag_t idle_streams;
ngx_msec_t buflen;
ngx_flag_t buffer;
ngx_pool_t *pool;
ngx_rtmp_live_stream_t *free_streams;
} ngx_rtmp_live_app_conf_t;

View file

@ -853,6 +853,9 @@ next:
static ngx_int_t
ngx_rtmp_log_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
{
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"log: ngx_rtmp_log_play");
ngx_rtmp_log_ctx_t *ctx;
if (s->auto_pushed || s->relay) {
@ -867,6 +870,8 @@ ngx_rtmp_log_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
ctx->play = 1;
next:
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"log: ngx_rtmp_log_play: next");
return next_play(s, v);
}

View file

@ -571,6 +571,7 @@ ngx_rtmp_netcall_http_format_session(ngx_rtmp_session_t *s, ngx_pool_t *pool)
ngx_chain_t *cl;
ngx_buf_t *b;
ngx_str_t *addr_text;
size_t bsize;
addr_text = &s->connection->addr_text;
@ -579,16 +580,34 @@ ngx_rtmp_netcall_http_format_session(ngx_rtmp_session_t *s, ngx_pool_t *pool)
return NULL;
}
b = ngx_create_temp_buf(pool,
sizeof("app=") - 1 + s->app.len * 3 +
sizeof("&flashver=") - 1 + s->flashver.len * 3 +
sizeof("&swfurl=") - 1 + s->swf_url.len * 3 +
sizeof("&tcurl=") - 1 + s->tc_url.len * 3 +
sizeof("&pageurl=") - 1 + s->page_url.len * 3 +
sizeof("&addr=") - 1 + addr_text->len * 3 +
sizeof("&clientid=") - 1 + NGX_INT_T_LEN
);
/**
* @2016-04-20 sergey-dryabzhinsky
* Not all params may be filled in session
* So not override them with empty values
*/
bsize = sizeof("addr=") - 1 + addr_text->len * 3 +
sizeof("&clientid=") - 1 + NGX_INT_T_LEN;
// Indicator of additional vars from session
// Event `connect` don't have them, for example
if (s->app.len) {
bsize += sizeof("&app=") - 1 + s->app.len * 3;
}
if (s->flashver.len) {
bsize += sizeof("&flashver=") - 1 + s->flashver.len * 3;
}
if (s->swf_url.len) {
bsize += sizeof("&swfurl=") - 1 + s->swf_url.len * 3;
}
if (s->tc_url.len) {
bsize += sizeof("&tcurl=") - 1 + s->tc_url.len * 3;
}
if (s->page_url.len) {
bsize += sizeof("&pageurl=") - 1 + s->page_url.len * 3;
}
b = ngx_create_temp_buf(pool, bsize);
if (b == NULL) {
return NULL;
}
@ -596,31 +615,7 @@ ngx_rtmp_netcall_http_format_session(ngx_rtmp_session_t *s, ngx_pool_t *pool)
cl->buf = b;
cl->next = NULL;
b->last = ngx_cpymem(b->last, (u_char*) "app=", sizeof("app=") - 1);
b->last = (u_char*) ngx_escape_uri(b->last, s->app.data, s->app.len,
NGX_ESCAPE_ARGS);
b->last = ngx_cpymem(b->last, (u_char*) "&flashver=",
sizeof("&flashver=") - 1);
b->last = (u_char*) ngx_escape_uri(b->last, s->flashver.data,
s->flashver.len, NGX_ESCAPE_ARGS);
b->last = ngx_cpymem(b->last, (u_char*) "&swfurl=",
sizeof("&swfurl=") - 1);
b->last = (u_char*) ngx_escape_uri(b->last, s->swf_url.data,
s->swf_url.len, NGX_ESCAPE_ARGS);
b->last = ngx_cpymem(b->last, (u_char*) "&tcurl=",
sizeof("&tcurl=") - 1);
b->last = (u_char*) ngx_escape_uri(b->last, s->tc_url.data,
s->tc_url.len, NGX_ESCAPE_ARGS);
b->last = ngx_cpymem(b->last, (u_char*) "&pageurl=",
sizeof("&pageurl=") - 1);
b->last = (u_char*) ngx_escape_uri(b->last, s->page_url.data,
s->page_url.len, NGX_ESCAPE_ARGS);
b->last = ngx_cpymem(b->last, (u_char*) "&addr=", sizeof("&addr=") - 1);
b->last = ngx_cpymem(b->last, (u_char*) "addr=", sizeof("addr=") - 1);
b->last = (u_char*) ngx_escape_uri(b->last, addr_text->data,
addr_text->len, NGX_ESCAPE_ARGS);
@ -628,6 +623,36 @@ ngx_rtmp_netcall_http_format_session(ngx_rtmp_session_t *s, ngx_pool_t *pool)
sizeof("&clientid=") - 1);
b->last = ngx_sprintf(b->last, "%ui", (ngx_uint_t) s->connection->number);
if (s->app.len) {
b->last = ngx_cpymem(b->last, (u_char*) "&app=", sizeof("&app=") - 1);
b->last = (u_char*) ngx_escape_uri(b->last, s->app.data, s->app.len,
NGX_ESCAPE_ARGS);
}
if (s->flashver.len) {
b->last = ngx_cpymem(b->last, (u_char*) "&flashver=",
sizeof("&flashver=") - 1);
b->last = (u_char*) ngx_escape_uri(b->last, s->flashver.data,
s->flashver.len, NGX_ESCAPE_ARGS);
}
if (s->swf_url.len) {
b->last = ngx_cpymem(b->last, (u_char*) "&swfurl=",
sizeof("&swfurl=") - 1);
b->last = (u_char*) ngx_escape_uri(b->last, s->swf_url.data,
s->swf_url.len, NGX_ESCAPE_ARGS);
}
if (s->tc_url.len) {
b->last = ngx_cpymem(b->last, (u_char*) "&tcurl=",
sizeof("&tcurl=") - 1);
b->last = (u_char*) ngx_escape_uri(b->last, s->tc_url.data,
s->tc_url.len, NGX_ESCAPE_ARGS);
}
if (s->page_url.len) {
b->last = ngx_cpymem(b->last, (u_char*) "&pageurl=",
sizeof("&pageurl=") - 1);
b->last = (u_char*) ngx_escape_uri(b->last, s->page_url.data,
s->page_url.len, NGX_ESCAPE_ARGS);
}
return cl;
}

File diff suppressed because it is too large Load diff

View file

@ -716,6 +716,9 @@ ngx_rtmp_play_parse_index(char type, u_char *args)
static ngx_int_t
ngx_rtmp_play_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
{
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"play: ngx_rtmp_play_play");
ngx_rtmp_play_main_conf_t *pmcf;
ngx_rtmp_play_app_conf_t *pacf;
ngx_rtmp_play_ctx_t *ctx;
@ -827,9 +830,15 @@ ngx_rtmp_play_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"play: fmt=%V", &ctx->fmt->name);
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"play: ngx_rtmp_play_play: next_entry");
return ngx_rtmp_play_next_entry(s, v);
next:
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"play: ngx_rtmp_play_play: next");
return next_play(s, v);
}
@ -837,6 +846,9 @@ next:
static ngx_int_t
ngx_rtmp_play_next_entry(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
{
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"play: ngx_rtmp_play_next_entry");
ngx_rtmp_play_app_conf_t *pacf;
ngx_rtmp_play_ctx_t *ctx;
ngx_rtmp_play_entry_t *pe;
@ -881,6 +893,9 @@ ngx_rtmp_play_next_entry(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
/* open remote */
if (pe->url) {
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"play: ngx_rtmp_play_next_entry: open remote");
return ngx_rtmp_play_open_remote(s, v);
}
@ -917,12 +932,19 @@ ngx_rtmp_play_next_entry(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
"play: open local file '%s'", path);
if (ngx_rtmp_play_open(s, v->start) != NGX_OK) {
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"play: ngx_rtmp_play_next_entry: error open");
return NGX_ERROR;
}
break;
}
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"play: ngx_rtmp_play_next_entry: next");
return next_play(s, v);
}
@ -1048,6 +1070,9 @@ ngx_rtmp_play_remote_create(ngx_rtmp_session_t *s, void *arg, ngx_pool_t *pool)
static ngx_int_t
ngx_rtmp_play_remote_handle(ngx_rtmp_session_t *s, void *arg, ngx_chain_t *in)
{
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"play: ngx_rtmp_play_remote_handle");
ngx_rtmp_play_t *v = arg;
ngx_rtmp_play_ctx_t *ctx;
@ -1055,6 +1080,8 @@ ngx_rtmp_play_remote_handle(ngx_rtmp_session_t *s, void *arg, ngx_chain_t *in)
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_play_module);
if (ctx->nbody == 0) {
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"play: ngx_rtmp_play_remote_handle: next_entry");
return ngx_rtmp_play_next_entry(s, v);
}
@ -1066,9 +1093,16 @@ ngx_rtmp_play_remote_handle(ngx_rtmp_session_t *s, void *arg, ngx_chain_t *in)
"play: open remote file");
if (ngx_rtmp_play_open(s, v->start) != NGX_OK) {
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"play: ngx_rtmp_play_remote_handle: error open");
return NGX_ERROR;
}
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"play: ngx_rtmp_play_remote_handle: next");
return next_play(s, (ngx_rtmp_play_t *)arg);
}

View file

@ -13,6 +13,7 @@
#include "ngx_rtmp_record_module.h"
ngx_rtmp_record_started_pt ngx_rtmp_record_started;
ngx_rtmp_record_done_pt ngx_rtmp_record_done;
@ -112,7 +113,7 @@ static ngx_command_t ngx_rtmp_record_commands[] = {
{ ngx_string("record_interval_size"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|
NGX_RTMP_REC_CONF|NGX_CONF_TAKE1,
NGX_RTMP_REC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_rtmp_record_app_conf_t, interval_size),
@ -856,6 +857,8 @@ ngx_rtmp_record_node_close(ngx_rtmp_session_t *s,
v.recorder = rracf->id;
ngx_rtmp_record_make_path(s, rctx, &v.path);
rctx->record_started = 0;
rc = ngx_rtmp_record_done(s, &v);
s->app_conf = app_conf;
@ -905,6 +908,21 @@ ngx_rtmp_record_write_frame(ngx_rtmp_session_t *s,
rctx->audio = 1;
}
if (rctx->record_started == 0)
{
rctx->record_started = 1;
ngx_rtmp_record_started_t v;
ngx_rtmp_record_app_conf_t *racf;
racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_record_module);
if (racf != NULL && racf->rec.nelts != 0) {
v.recorder = racf->id;
v.path = racf->path;
ngx_rtmp_record_started(s, &v);
}
}
timestamp = h->timestamp - rctx->epoch;
if ((int32_t) timestamp < 0) {
@ -990,14 +1008,6 @@ ngx_rtmp_record_write_frame(ngx_rtmp_session_t *s,
ngx_rtmp_record_node_close(s, rctx);
}
/* watch size interval */
if ((rracf->interval_size && rctx->file.offset >= (ngx_int_t) rracf->interval_size) ||
(rracf->max_frames && rctx->nframes >= rracf->max_frames))
{
ngx_rtmp_record_node_close(s, rctx);
ngx_rtmp_record_node_open(s, rctx);
}
return NGX_OK;
}
@ -1070,9 +1080,9 @@ ngx_rtmp_record_node_avd(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx,
return NGX_OK;
}
if (rctx->file.fd == NGX_INVALID_FILE) {
/*if (rctx->file.fd == NGX_INVALID_FILE) {
return NGX_OK;
}
}*/
if (h->type == NGX_RTMP_MSG_AUDIO &&
(rracf->flags & NGX_RTMP_RECORD_AUDIO) == 0)
@ -1093,9 +1103,9 @@ ngx_rtmp_record_node_avd(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx,
return NGX_OK;
}
// record interval should work if set, manual mode or not
if (rracf->interval != (ngx_msec_t) NGX_CONF_UNSET) {
if (brkframe && rracf->interval != NGX_CONF_UNSET_MSEC)
{
// record interval should work if set, manual mode or not
next = rctx->last;
next.msec += rracf->interval;
next.sec += (next.msec / 1000);
@ -1104,12 +1114,13 @@ ngx_rtmp_record_node_avd(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx,
if (ngx_cached_time->sec > next.sec ||
(ngx_cached_time->sec == next.sec &&
ngx_cached_time->msec > next.msec))
{
ngx_rtmp_record_node_close(s, rctx);
ngx_rtmp_record_node_open(s, rctx);
}
} else if (!rctx->failed) {
{
ngx_rtmp_record_node_close(s, rctx);
ngx_rtmp_record_node_open(s, rctx);
}
}
else if (!rctx->failed)
{
ngx_rtmp_record_node_open(s, rctx);
}
@ -1208,6 +1219,12 @@ ngx_rtmp_record_node_avd(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx,
}
static ngx_int_t
ngx_rtmp_record_started_init(ngx_rtmp_session_t *s, ngx_rtmp_record_started_t *v)
{
return NGX_OK;
}
static ngx_int_t
ngx_rtmp_record_done_init(ngx_rtmp_session_t *s, ngx_rtmp_record_done_t *v)
{
@ -1222,6 +1239,7 @@ ngx_rtmp_record_recorder(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
ngx_int_t i;
ngx_str_t *value;
ngx_conf_t save;
ngx_module_t **modules;
ngx_rtmp_module_t *module;
ngx_rtmp_core_app_conf_t *cacf, **pcacf, *rcacf;
ngx_rtmp_record_app_conf_t *racf, **pracf, *rracf;
@ -1248,17 +1266,22 @@ ngx_rtmp_record_recorder(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
return NGX_CONF_ERROR;
}
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_RTMP_MODULE) {
#if defined(nginx_version) && nginx_version >= 1009011
modules = cf->cycle->modules;
#else
modules = ngx_modules;
#endif
for (i = 0; modules[i]; i++) {
if (modules[i]->type != NGX_RTMP_MODULE) {
continue;
}
module = ngx_modules[i]->ctx;
module = modules[i]->ctx;
if (module->create_app_conf) {
ctx->app_conf[ngx_modules[i]->ctx_index] =
ctx->app_conf[modules[i]->ctx_index] =
module->create_app_conf(cf);
if (ctx->app_conf[ngx_modules[i]->ctx_index] == NULL) {
if (ctx->app_conf[modules[i]->ctx_index] == NULL) {
return NGX_CONF_ERROR;
}
}
@ -1302,6 +1325,8 @@ ngx_rtmp_record_postconfiguration(ngx_conf_t *cf)
ngx_rtmp_core_main_conf_t *cmcf;
ngx_rtmp_handler_pt *h;
ngx_rtmp_record_started = ngx_rtmp_record_started_init;
ngx_rtmp_record_done = ngx_rtmp_record_done_init;
cmcf = ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_core_module);

View file

@ -54,6 +54,7 @@ typedef struct {
unsigned video_key_sent:1;
unsigned audio:1;
unsigned video:1;
unsigned record_started:1;
} ngx_rtmp_record_rec_ctx_t;
@ -84,10 +85,23 @@ typedef struct {
} ngx_rtmp_record_done_t;
typedef struct {
ngx_str_t recorder;
ngx_str_t path;
} ngx_rtmp_record_started_t;
typedef ngx_int_t (*ngx_rtmp_record_started_pt)(ngx_rtmp_session_t *s,
ngx_rtmp_record_started_t *v);
typedef ngx_int_t (*ngx_rtmp_record_done_pt)(ngx_rtmp_session_t *s,
ngx_rtmp_record_done_t *v);
extern ngx_rtmp_record_started_pt ngx_rtmp_record_started;
extern ngx_rtmp_record_done_pt ngx_rtmp_record_done;

View file

@ -710,6 +710,9 @@ next:
static ngx_int_t
ngx_rtmp_relay_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
{
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"relay: ngx_rtmp_relay_play");
ngx_rtmp_relay_app_conf_t *racf;
ngx_rtmp_relay_target_t *target, **t;
ngx_str_t name;
@ -751,6 +754,9 @@ ngx_rtmp_relay_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
}
next:
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"relay: ngx_rtmp_relay_play: next");
return next_play(s, v);
}

View file

@ -3,8 +3,6 @@
* Copyright (C) Roman Arutyunyan
*/
#include <sys/time.h>
#include <ngx_config.h>
#include <ngx_core.h>
#include "ngx_rtmp.h"
@ -605,9 +603,10 @@ ngx_rtmp_create_redirect_status(ngx_rtmp_session_t *s, char *callMethod, char *d
static double dtrans;
static double dcode;
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
ngx_log_debug0(NGX_LOG_DEBUG, s->connection->log, 0,
"create redirect status: got data");
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
ngx_log_debug5(NGX_LOG_DEBUG, s->connection->log, 0,
"create redirect status: method='%s', status code='%s' level='%s' "
"ex.code=%ui ex.redirect='%s'", callMethod,
"NetConnection.Connect.Rejected", "error", 302, to_url.data);
@ -663,7 +662,7 @@ ngx_rtmp_create_redirect_status(ngx_rtmp_session_t *s, char *callMethod, char *d
sizeof(out_inf) },
};
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
ngx_log_debug0(NGX_LOG_DEBUG, s->connection->log, 0,
"create redirect status: set structure data");
out_elts[0].data = callMethod;
@ -709,7 +708,7 @@ ngx_rtmp_create_close_method(ngx_rtmp_session_t *s, char *methodName)
&dtrans, 0 },
};
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
ngx_log_debug0(NGX_LOG_DEBUG, s->connection->log, 0,
"create close method: set structure data");
out_elts[0].data = methodName;
@ -775,7 +774,7 @@ ngx_rtmp_create_fcpublish(ngx_rtmp_session_t *s, u_char *desc)
sizeof(out_inf) },
};
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
ngx_log_debug0(NGX_LOG_DEBUG, s->connection->log, 0,
"create: fcpublish - set structure data");
out_inf[2].data = desc;
@ -842,7 +841,7 @@ ngx_rtmp_create_fcunpublish(ngx_rtmp_session_t *s, u_char *desc)
sizeof(out_inf) },
};
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
ngx_log_debug0(NGX_LOG_DEBUG, s->connection->log, 0,
"create: fcunpublish - set structure data");
out_inf[2].data = desc;
@ -876,10 +875,8 @@ ngx_rtmp_create_fi(ngx_rtmp_session_t *s)
struct tm tm;
struct timeval tv;
struct timezone tz;
int errfl;
static u_char buf_time[NGX_TIME_T_LEN + 1];
static u_char buf_time[NGX_TIME_T_LEN*2 + 1];
static u_char buf_date[NGX_TIME_T_LEN + 1];
@ -898,7 +895,7 @@ ngx_rtmp_create_fi(ngx_rtmp_session_t *s)
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
"onFi", 0 },
"onFI", 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_null_string,
@ -916,22 +913,16 @@ ngx_rtmp_create_fi(ngx_rtmp_session_t *s)
trans = 0;
errfl = gettimeofday(&tv, &tz);
if (errfl) {
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"create: fi - can't get time!");
return NULL;
}
ngx_gettimeofday(&tv);
ngx_libc_localtime((time_t)tv.tv_sec, &tm);
ngx_memzero(buf_time, sizeof(buf_time));
ngx_memzero(buf_date, sizeof(buf_date));
errfl = sprintf((char *)buf_time, "%02d:%02d:%02d.%d", tm.tm_hour, tm.tm_min, tm.tm_sec, (int)tv.tv_usec);
ngx_sprintf(buf_time, "%02d:%02d:%02d.%06d", tm.tm_hour, tm.tm_min, tm.tm_sec, (int)tv.tv_usec);
// Strange order, but FMLE send like this
errfl = sprintf((char *)buf_date, "%02d-%02d-%04d", tm.tm_mday, tm.tm_mon + 1, tm.tm_year + 1900);
ngx_sprintf(buf_date, "%02d-%02d-%04d", tm.tm_mday, tm.tm_mon + 1, tm.tm_year + 1900);
out_inf[0].data = buf_time;
out_inf[1].data = buf_date;

View file

@ -66,7 +66,8 @@ ngx_rtmp_free_shared_chain(ngx_rtmp_core_srv_conf_t *cscf, ngx_chain_t *in)
}
for (cl = in; ; cl = cl->next) {
if (cl->next == NULL) {
/* FIXME: Don't create circular chains in the first place */
if (cl->next == NULL || cl->next == in) {
cl->next = cscf->free;
cscf->free = in;
return;

View file

@ -349,8 +349,12 @@ ngx_rtmp_stat_client(ngx_http_request_t *r, ngx_chain_t ***lll,
*/
NGX_RTMP_STAT_L("<port>");
sa = (struct sockaddr_in *) s->connection->sockaddr;
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%ui",
if (sa) {
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%ui",
(ngx_uint_t) ntohs(sa->sin_port)) - buf);
} else {
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%ui", 0) - buf);
}
NGX_RTMP_STAT_L("</port>");
@ -551,7 +555,17 @@ ngx_rtmp_stat_live(ngx_http_request_t *r, ngx_chain_t ***lll,
NGX_RTMP_STAT_L("</height><frame_rate>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%.3f", codec->frame_rate) - buf);
NGX_RTMP_STAT_L("</frame_rate>");
NGX_RTMP_STAT_L("</frame_rate><data_rate>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%.0f", codec->video_data_rate) - buf);
NGX_RTMP_STAT_L("</data_rate>");
if(codec->video_keyframe_frequency) {
NGX_RTMP_STAT_L("<keyframe_frequency>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%.0f", codec->video_keyframe_frequency) - buf);
NGX_RTMP_STAT_L("</keyframe_frequency>");
}
cname = ngx_rtmp_get_video_codec_name(codec->video_codec_id);
if (*cname) {
@ -611,6 +625,12 @@ ngx_rtmp_stat_live(ngx_http_request_t *r, ngx_chain_t ***lll,
"%ui", codec->sample_rate) - buf);
NGX_RTMP_STAT_L("</sample_rate>");
}
if (codec->audio_data_rate) {
NGX_RTMP_STAT_L("<data_rate>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%0.0f", codec->audio_data_rate) - buf);
NGX_RTMP_STAT_L("</data_rate>");
}
NGX_RTMP_STAT_L("</audio>");
NGX_RTMP_STAT_L("</meta>\r\n");
@ -813,7 +833,10 @@ ngx_rtmp_stat_handler(ngx_http_request_t *r)
#ifdef NGX_COMPILER
NGX_RTMP_STAT_L("<compiler>" NGX_COMPILER "</compiler>\r\n");
#endif
/* This may prevent reproducible builds. If you need that info - pass `-DNGX_BUILD_DATEITIME=1` to CFLAGS */
#ifdef NGX_BUILD_DATEITIME
NGX_RTMP_STAT_L("<built>" __DATE__ " " __TIME__ "</built>\r\n");
#endif
NGX_RTMP_STAT_L("<pid>");
NGX_RTMP_STAT(nbuf, ngx_snprintf(nbuf, sizeof(nbuf),

View file

@ -8,8 +8,8 @@
#define _NGX_RTMP_VERSION_H_INCLUDED_
#define nginx_rtmp_version 1001007
#define NGINX_RTMP_VERSION "1.1.7.9"
#define nginx_rtmp_version 1002002
#define NGINX_RTMP_VERSION "1.2.2-r1"
#endif /* _NGX_RTMP_VERSION_H_INCLUDED_ */