Compare commits

...

517 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
Sergey Dryabzhinsky 17d5c4678d Version up 1.1.7.9
- fix fork author
2015-09-23 16:28:02 +03:00
Sergey c9442e9aa3 Merge pull request #46 from sergey-dryabzhinsky/fixes-for-42-review-merged-code
Fixes for 42 review merged code
2015-09-23 16:26:28 +03:00
Sergey Dryabzhinsky f9c89a2e21 Fix type warnings 2015-09-22 22:18:24 +03:00
Sergey Dryabzhinsky f1cde7d4a4 Missing fixes from upstreams 2015-09-20 06:35:48 +03:00
Sergey 9faaa99829 Merge pull request #45 from sergey-dryabzhinsky/fixes-for-42-frame-rate-not-double
Fixes for 42 frame rate not double
2015-09-20 00:24:36 +03:00
Sergey Dryabzhinsky 509a9e2f75 Fix data type for frame rate and duration 2015-09-20 00:22:50 +03:00
Sergey Dryabzhinsky f8097173ab Frame rate are double not int 2015-09-19 03:15:38 +03:00
Sergey bacdd48aac Merge pull request #43 from sergey-dryabzhinsky/live-on-fi-handler
Send system (server) date & time on Fi call
2015-09-19 03:04:23 +03:00
Sergey Dryabzhinsky b666598e3a Fix onFi handler 2015-09-19 03:03:13 +03:00
Sergey Dryabzhinsky a344ee6607 Append support of Fi command. Return system date and time. 2015-09-19 01:16:58 +03:00
Sergey 7abda68ef7 Merge pull request #44 from sergey-dryabzhinsky/dummy-fcpublish-response
Fix fcpublish logs - no segfault here
2015-09-19 01:16:12 +03:00
Sergey Dryabzhinsky 754cca2a58 Fix fcpublish logs - no segfault here 2015-09-19 00:08:45 +03:00
Sergey 0d0882b29d Merge pull request #39 from sergey-dryabzhinsky/dummy-fcpublish-response
Add dummy response for onFCPublish
2015-09-18 22:15:18 +03:00
Sergey Dryabzhinsky 83cb5a6f90 Seems like action name is readed before. Try to fix reading string length. 2015-09-18 21:18:50 +03:00
Sergey Dryabzhinsky 5b62a17516 Remove ctx checks - not in context? Not used thou. 2015-09-18 19:56:13 +03:00
Sergey Dryabzhinsky 498ff9a468 Fix unused vars 2015-09-18 19:30:56 +03:00
Sergey Dryabzhinsky 091936ea2d Trying to recieve fc(un)publish data - there is no stream at the moment. 2015-09-18 19:10:38 +03:00
Sergey Dryabzhinsky b4bbfff24b Change magick numbers in send module 2015-09-18 02:08:50 +03:00
Sergey Dryabzhinsky 5d6ea2314f Change magick numbers 2015-09-18 01:56:10 +03:00
Sergey Dryabzhinsky 100ecc4fe8 Second version of callbacks:
- more debug output
- moved code from send module
2015-09-18 00:35:26 +03:00
Sergey Dryabzhinsky 8fda94268c Fix callback names, add FCUnpublish callback 2015-09-17 21:14:02 +03:00
Sergey Dryabzhinsky 414053862c Add amf sender for FCPublish 2015-09-17 20:12:24 +03:00
Sergey Dryabzhinsky f29fbc89a4 Add dummy response for onFCPublish 2015-09-17 14:13:29 +03:00
Sergey bd154e391b Merge pull request #35 from sergey-dryabzhinsky/new-notify-http-codes-4xx
Add first version of 4xx HTTP code handlers for notify submodule
2015-09-17 13:23:56 +03:00
Sergey Dryabzhinsky f0c9b595bc Fix missing vars in func calls 2015-09-17 12:57:33 +03:00
Sergey Dryabzhinsky c39589c5ea Add first version of 4xx HTTP code handlers for nitify submodule 2015-08-10 22:36:43 +03:00
Sergey 854d5142df Merge pull request #34 from sergey-dryabzhinsky/fix-gcc-warnings2
Forgotten fix for 'close' connection call
2015-08-10 22:14:59 +03:00
Sergey Dryabzhinsky 5504e4636c Forgotten fix for 'close' connection call 2015-08-10 22:13:36 +03:00
Sergey Dryabzhinsky a92851011a A little version up - 1.1.7.8 2015-08-08 15:41:22 +03:00
Sergey b1e28ead5b Merge pull request #32 from TrurlMcByte/master
generate nessesary subfolders in play_local_path
2015-08-08 01:32:39 +03:00
Sergey 335bf07021 Merge pull request #31 from sergey-dryabzhinsky/fix-gcc-warnings
Fix gcc pedantic build warnings/errors
2015-08-08 01:28:51 +03:00
Sergey Dryabzhinsky 24be2c72cd Fix gcc pedantic build warnings/errors
- use ngx_memcpy if return value not needed
- remove some unused vars
- add new function definitions for send submodule
2015-08-08 00:57:13 +03:00
Sergey Dryabzhinsky 7f24b5f6cd A little version up 1.1.7.7 2015-08-06 10:04:51 +03:00
Sergey bcbef42a7c Merge pull request #26 from sergey-dryabzhinsky/add-publisher-redirect-support
Add publisher redirect support, partially
2015-08-06 10:03:37 +03:00
Sergey b3f0fb0460 Merge pull request #25 from mainyaa/fix-datetime-system
Fix hls_datetime system bug
2015-08-06 09:55:52 +03:00
mainyaa 5ed7825c0c Fix datetime system bug 2015-08-06 15:44:32 +09:00
Sergey Dryabzhinsky bb67548636 Update:
- fix config option parse
- append redirect support in on_connect handler
- add support of rtmpdump handlers for RTMP commands
- try to catch with FMLE
2015-08-01 19:30:01 +03:00
Sergey Dryabzhinsky 3497601b6b Upsate:
- fix memory allocation for URL string
- fix code pass in AMF
2015-08-01 04:50:38 +03:00
Trurl McByte 38f98aa81c recrete fix on last build 2015-07-21 12:10:40 +03:00
Sergey Dryabzhinsky 139c8c2773 Try to fix AMF packet creation 2015-07-19 22:42:07 +03:00
Sergey Dryabzhinsky 8409e4c499 Fix signed-unsigned warnings 2015-07-12 20:01:53 +03:00
Sergey Dryabzhinsky cb439496eb Add forgotten config flag 2015-07-07 16:35:21 +03:00
Sergey Dryabzhinsky ec3684f9f9 Description not needed here 2015-07-07 16:15:53 +03:00
Sergey Dryabzhinsky 0327cbf651 New function for notify:
- add 302 redirect via onStatus of NetConnection
- new option for notify - notify_send_redirect = 0/1
2015-07-07 16:04:31 +03:00
Sergey Dryabzhinsky ae7974bc2d Little version up 2015-07-05 08:35:04 +03:00
Sergey f455876a9c Merge pull request #24 from sergey-dryabzhinsky/show-recording-status
Show recording status
2015-07-05 08:34:36 +03:00
Sergey Dryabzhinsky 4e17054a6d Fix missing var, check if record have opened file 2015-07-05 08:12:33 +03:00
Sergey Dryabzhinsky dec648681d Add recording status to stat page
Update rtmp status page with streams and clients recording status
2015-07-05 07:52:41 +03:00
Sergey Dryabzhinsky 46c0fe6d5a Merge branch 'master' of https://github.com/sergey-dryabzhinsky/nginx-rtmp-module 2015-07-05 04:19:24 +03:00
Sergey Dryabzhinsky 3207278846 Little version up 2015-07-05 04:19:06 +03:00
Sergey 2a68430ae2 Merge pull request #23 from sergey-dryabzhinsky/add-app-name-no-notify-params
Add application name into params for all notify
2015-07-05 04:18:38 +03:00
Sergey Dryabzhinsky 70d056bf92 Add application name into params for all notify
- append application name into all notify events data
2015-07-05 04:16:18 +03:00
Sergey Dryabzhinsky 658f26fc2c Merge branch 'master' of https://github.com/sergey-dryabzhinsky/nginx-rtmp-module 2015-07-05 03:54:47 +03:00
Sergey Dryabzhinsky 4da120a125 Little version up 2015-07-05 03:47:42 +03:00
Sergey Dryabzhinsky fb80463ba9 Little version up 2015-07-05 03:43:42 +03:00
Sergey 21683a4408 Merge pull request #22 from sergey-dryabzhinsky/fix-record-interval-for-all-modes
Fix record interal
2015-07-05 03:43:17 +03:00
Sergey Dryabzhinsky 25146e40f9 Fix record interal
- it should work if set. No matter record mode is.
2015-07-05 03:41:06 +03:00
Sergey Dryabzhinsky e9f58daf81 Little version bump 2015-07-05 02:00:43 +03:00
Sergey Dryabzhinsky 3170037e74 Fix getting client connection port 2015-07-05 02:00:14 +03:00
Sergey Dryabzhinsky dc0b3df1b7 Little version up 2015-07-04 23:07:01 +03:00
Sergey 52b09f5300 Merge pull request #21 from sergey-dryabzhinsky/fix-port-stat-view
Use client connection socket port for info
2015-07-04 23:05:57 +03:00
Sergey Dryabzhinsky ac4197b871 Use client connection socket port for info 2015-07-04 23:04:18 +03:00
Sergey Dryabzhinsky b49c7fb6ea Fix version number 2015-07-04 09:04:14 +03:00
Sergey 5d4dfaee43 Merge pull request #20 from dailymotion/DEVSYS-261
App name was not copied when creating relay
2015-07-03 02:49:38 +03:00
Sergey 1e5b135b2b Merge pull request #15 from itpp16/patch-2
Update ngx_rtmp_handler.c
2015-07-03 02:45:50 +03:00
Sergey 889e6afccb Merge pull request #19 from sergey-dryabzhinsky/fmle-onfi-command
Add onFi data command for live stream.
2015-07-03 02:15:55 +03:00
Sergey Dryabzhinsky 99cf8a1a10 Add onFi data command for live stream. It is sended by FMLE with timestamps. 2015-07-03 02:15:04 +03:00
Sergey 1055727de8 Merge pull request #18 from dailymotion/DEVSYS-260
Display socket port number for each rtmp connection
2015-07-03 02:06:21 +03:00
Sergey de8faa55a7 Merge pull request #17 from Eugaulerss/patch-1
Update ngx_rtmp_handler.c
2015-07-03 02:05:13 +03:00
Sergey c9e70d5994 Merge pull request #16 from itpp16/patch-4
Update ngx_rtmp_play_module.c
2015-07-03 02:04:36 +03:00
Sergey 593368e9e6 Merge pull request #14 from LanetNetwork/master
Added Directive "record_interval_size"
2015-07-03 02:02:42 +03:00
Sergey a05b3babfe Merge pull request #13 from hannseman/master
Remove unneeded space in EXT-X-PLAYLIST-TYPE tag
2015-07-03 02:00:08 +03:00
Sergey 961fde23f7 Merge pull request #12 from sqbing/patch-1
Update ngx_rtmp_codec_module.c
2015-07-03 01:55:52 +03:00
Sergey 75fe5406b4 Merge pull request #11 from Florob/ipv6-worker
Fix access to config field of ngx_rtmp_in{,6}_addr_t.  Closes #290
2015-07-03 01:51:39 +03:00
Sergey 1616a378ef Merge pull request #10 from cine-io/data-channel-live-record
Data channel live record
2015-07-03 01:49:46 +03:00
Sergey 4c1b69b5bb Merge pull request #9 from cine-io/relay-push-set-data-frame
Relay push set data frame
2015-07-03 01:48:30 +03:00
Sergey Dryabzhinsky 038c3bbc67 Merge branch 'cine-io-dont-overwrite-with-invalid-metadata' 2015-07-03 01:36:40 +03:00
Sergey Dryabzhinsky 0d77210815 Merge branch 'dont-overwrite-with-invalid-metadata' of https://github.com/cine-io/nginx-rtmp-module into cine-io-dont-overwrite-with-invalid-metadata 2015-07-03 01:35:53 +03:00
Sergey c4ee944870 Merge pull request #7 from steelywing/master
fix 'volatile' qualifiers error
2015-07-03 01:25:24 +03:00
Sergey 69d79ccda6 Merge pull request #6 from odiszapc/master
Add in/out traffice per client
2015-07-03 01:24:04 +03:00
Sergey 859540db60 Merge pull request #5 from berg/parse-scaling-list
Parse scaling list from SPS
2015-07-03 01:22:50 +03:00
Sergey 087e5358e2 Merge pull request #4 from berg/dash-frame-rate
Don't truncate framerate for MPEG-DASH
2015-07-03 01:21:26 +03:00
Sergey d797d269a4 Merge pull request #3 from petergeneric/master
Allow the filename on disk to use no suffix
2015-07-03 01:19:57 +03:00
Sergey 95471ae880 Merge pull request #2 from jbochi/program-date-time
Program date time
2015-07-03 01:13:46 +03:00
Sergey a079875fd7 Merge pull request #1 from sergey-dryabzhinsky/hls-mp3-support-backport
Backport support MP3 for HLS from abandoned pull request
2015-07-03 01:05:51 +03:00
Sergey Dryabzhinsky 8341644121 Backport support MP3 for HLS from abandoned pull request 2015-07-03 01:03:39 +03:00
Sellier Alexis 098ded3e87 App name was not copied when creating relay 2015-06-03 16:29:34 +02:00
Sellier Alexis 70c90bbac5 Display socket port number for each rtmp connection 2015-06-02 18:01:10 +02:00
Eugaulerss a4907e80ce Update ngx_rtmp_handler.c
fixbug lost data when chunk_size changing, the frontier of stream data should by pointed by the pos and last of ngx_buf_t
2015-05-28 16:47:23 +08:00
itpp16 48983747d3 Update ngx_rtmp_play_module.c 2015-04-09 12:58:46 +02:00
itpp16 6ce821727d Update ngx_rtmp_play_module.c
fixed compilation with nginx 1.7.12
2015-04-09 12:57:07 +02:00
itpp16 809f094124 Update ngx_rtmp_handler.c
fixed compilation with nginx 1.7.12
2015-04-09 12:47:32 +02:00
Anton Baranov 2c2e90b706 add Directive record_interval_size 2015-04-08 11:08:17 +03:00
Anton Baranov b053eca1f9 add Directive record_interval_size 2015-04-08 11:06:54 +03:00
Anton Baranov b92a262f4c add Directive record_interval_size 2015-04-08 11:00:03 +03:00
Hannes Ljungberg 955c7e6b64 Remove unneeded space in EXT-X-PLAYLIST-TYPE tag 2015-03-27 13:31:40 +01:00
Roman Arutyunyan f62a083806 fixed compilation with nginx 1.7.11 2015-03-23 22:30:37 +03:00
BeenWoo cb36f9c23c Update ngx_rtmp_codec_module.c
Some encoders send "setDataFrame" instead of "@setDataFrame", which makes rtmp module can't show correct video frame rate in stat page.
2015-02-03 15:37:46 +08:00
Florian Zeitz 1d1d4fcac6 Fix access to config field of ngx_rtmp_in{,6}_addr_t. Closes #290 2014-11-28 19:19:52 +01:00
Jeffrey Wescott 459e730d34 don't send @setDataFrame unless we've already received NetStream.Publish.Start 2014-11-05 17:16:21 -08:00
Jeffrey Wescott 8f0f2e75c9 We need to loop through _all_ of the ctx->play sessions when sending @setDataFrame 2014-10-30 21:44:26 -07:00
Jeffrey Wescott 17159755e5 Support for the data channel for the record module. 2014-10-30 18:20:54 -07:00
Jeffrey Wescott de42f3801d Support for onTextData and onCuePoint in the data channel for the live module. 2014-10-30 18:18:53 -07:00
Jeffrey Wescott 1d21d68c3c It's possible that we'll receive NetStream.Publish.Start _before_ the codec
module has parsed the meta data. To handle that case, we'll also try to call
@setDataFrame when we receive the @setDataFrame from the publisher.
2014-10-30 13:17:17 -07:00
Jeffrey Wescott 2a54c8e089 Adobe Media Server and the like require that metadata is sent using
@setDataFrame. As such, in the relay module, when propagating metadata to
another server, we will use the @setDataFrame command.
2014-10-29 17:22:22 -07:00
Jeffrey Wescott d069f36f86 to avoid writing invalid metadata, don't overwrite metadata from previous @setDataFrame / onMetaData invocations unless the metadata has actually changed 2014-10-27 18:46:55 -07:00
Wing 99433754df fix 'volatile' qualifiers error 2014-10-24 01:35:36 +08:00
Wing 2362acf45c fix 'volatile' qualifiers error 2014-10-24 01:34:12 +08:00
Juarez Bochi ad6c63c149 Fix the way date time is written to fd 2014-10-10 13:33:32 -03:00
Juarez Bochi c54b413b43 Add program datetime from system or stream timestamp 2014-10-10 13:33:32 -03:00
Roman Arutyunyan 7a35372e30 enabled static relay cleanup to fix static_relay+auto_push combination 2014-09-23 23:39:09 +04:00
Alexey Plotnik d99c069e8e Add in/out traffice per client (because <play> has no information about traffic itself) 2014-09-22 00:51:09 +11:00
Roman Arutyunyan 5fb4c99ca9 added queue init 2014-09-21 10:17:58 +04:00
Roman Arutyunyan e1f92b1409 Merge pull request #469 from itpp16/patch-1
Update ngx_rtmp_mpegts.c
2014-09-17 09:42:25 +04:00
Roman Arutyunyan dd5f2aa117 fixed compilation 2014-09-17 07:10:38 +04:00
itpp16 d574043a96 Update ngx_rtmp_mpegts.c
Fixes for "warning C4244: '=' : conversion from 'uint64_t' to 'u_char', possible loss of data"
2014-09-13 22:48:03 +02:00
Roman Arutyunyan c390521963 Merge branch 'master' of github.com:arut/nginx-rtmp-module 2014-09-09 20:45:57 +04:00
Roman Arutyunyan 83dc27d30a fixed issue with multiple access_logs 2014-09-09 20:45:06 +04:00
Bryan Berg 2262649c1f Don't truncate framerate for MPEG-DASH
When pushing 29.97fps RTMP streams, the manifest shows an
incorrect frame rate of "29", not "30000/1001" as it should be.
2014-09-07 13:03:58 -07:00
Bryan Berg 9ebfcc1978 Parse scaling list from SPS
This is required for the dimensions to be calcuated properly when
streaming using the high profile (at least for the encoder I'm
using). Without this, the scaling list isn't skipped over and
the parsed dimensions are incorrect.

When the dimensions are incorrect, the MPEG-DASH manifest is
generated incorrectly.
2014-09-07 12:48:56 -07:00
Roman Arutyunyan 0bb2323990 Merge pull request #458 from saintdev/for-upstream
Explicitly signal HE-AAC in DASH manifest
2014-09-01 22:44:12 +04:00
Roman Arutyunyan 7500b4bd90 fixed path allocation 2014-09-01 18:30:41 +04:00
Roman Arutyunyan eb1cfef69e zero fragments_per_key now means one key per stream 2014-09-01 18:01:28 +04:00
Roman Arutyunyan 0b3d545ff6 fixed return code 2014-09-01 17:21:13 +04:00
Roman Arutyunyan 5e1d735992 fixed encryption 2014-09-01 17:10:03 +04:00
Roman Arutyunyan 997f24c5c6 implemented key id recovery from iv 2014-09-01 16:39:10 +04:00
Roman Arutyunyan 8099f44828 optimized aes128 encryption in mpegts writer 2014-09-01 13:25:48 +04:00
Roman Arutyunyan 51ab327abf fixed style 2014-08-31 18:58:43 +04:00
Roman Arutyunyan 69c090d85f renamed directives 2014-08-31 18:50:04 +04:00
Roman Arutyunyan 2f82fa2e8e fixed hls key path 2014-08-31 14:48:48 +04:00
Roman Arutyunyan 773336e497 make aes iv big-endian 2014-08-31 13:53:46 +04:00
Nathan Caldwell 359d76b555 Explicitly signal HE-AAC in DASH manifest
This works around an issue in the Chromium MediaSource implementation[1].
Chromium will not play a video when the audio track uses HE-AAC, unless it is
explicitly signaled in the manifest.

[1]: https://code.google.com/p/chromium/issues/detail?id=370927#c3
2014-08-27 20:21:46 -06:00
Roman Arutyunyan 4bed919cbe read hls key from old playlist 2014-08-05 16:00:50 +04:00
Roman Arutyunyan 416931d631 added keys directory handling 2014-08-05 14:28:04 +04:00
Roman Arutyunyan eff973ce01 fixes in hls encryption 2014-08-05 11:26:19 +04:00
Roman Arutyunyan 53064a48f1 implemented hls key auto-generation 2014-08-05 00:35:27 +04:00
Peter Wright 80d7b1c905 Allow the filename on disk to use no suffix (or a different suffix from the format), as long as the format prefix is used (e.g. "mp4:test.mov") 2014-07-17 13:07:49 +00:00
Roman Arutyunyan 8acacd0d79 fixed skipping first key frame in mp4 streamer 2014-04-24 07:06:11 +04:00
Roman Arutyunyan 607a53842a fixed debug logging 2014-04-08 04:53:11 +04:00
Roman Arutyunyan 812e2fd7b1 fixed compilation with old nginx 2014-04-03 19:13:26 +04:00
Roman Arutyunyan 8c2229cce5 version bump 2014-04-03 07:13:12 +04:00
Roman Arutyunyan a72e33ea41 fixed proxy protocol event error 2014-03-31 11:03:30 +04:00
Roman Arutyunyan 4e780909b9 added proxy protocol support 2014-03-31 08:52:20 +04:00
Roman Arutyunyan 876de488b8 added epoch argument to on_connect 2014-03-05 11:34:33 +04:00
Roman Arutyunyan abb017225b fixed errors in control output; now returning http 206 instead of empty recorded file path 2014-03-04 16:02:09 +04:00
Roman Arutyunyan 96855b1413 moved vod sample access to fix flash security error 2014-03-03 15:47:03 +04:00
Roman Arutyunyan 6f768dc4eb version up 2014-02-28 17:11:25 +04:00
Roman Arutyunyan 668125888b Merge branch 'vod-sample-access' 2014-02-28 17:09:32 +04:00
Roman Arutyunyan b31539ce95 reset byte counter at 0xf0000000 to fix publishing from fmle 2014-02-25 14:59:26 +04:00
Roman Arutyunyan 0f337fe9a4 added rtmp sample access for vod 2014-02-21 23:15:41 +04:00
Roman Arutyunyan a43bafe3de added hls dicontinuity tag when forcing fragment split 2014-02-21 09:17:39 +04:00
Roman Arutyunyan dcf37b2c39 fixed forcing HLS fragment split 2014-02-20 22:57:22 +04:00
Roman Arutyunyan 87bc473cde Merge branch 'flv-append-fix' 2014-02-04 19:55:04 +04:00
Roman Arutyunyan c76d0fad44 Merge branch 'master' of github.com:arut/nginx-rtmp-module 2014-02-04 17:16:54 +04:00
Roman Arutyunyan 0210f7ca83 added timestamp cut in recorder to prevent negative (big unsigned) timestamps 2014-02-04 14:32:32 +04:00
Roman Arutyunyan 521f8998b0 implemented play2 time continuation 2014-02-03 11:16:01 +04:00
Roman Arutyunyan 434e6cfc06 Merge pull request #334 from hlamer/patch-1
Use same path in the comments and in the code
2014-01-31 08:52:00 -08:00
Andrei Kopats 04cbad5fda Use same path in the comments and in the code 2014-01-31 16:52:19 +02:00
Roman Arutyunyan 8d1b1c1243 fix flv append time shift 2014-01-31 10:39:08 +04:00
Roman Arutyunyan 1474aec731 Merge branch 'flv-header-mask' 2014-01-27 17:46:39 +04:00
Roman Arutyunyan 78dcfbcc35 ensure hls directory every time before opening fragment 2014-01-26 00:55:49 +04:00
Roman Arutyunyan 546c42efeb added hls_fragment_naming_granularity 2014-01-24 01:41:54 +04:00
Roman Arutyunyan 2eb36e3d7d fixed hls crash in stream eof 2014-01-23 07:47:58 +04:00
Roman Arutyunyan acc1aa789f fixed crash in hls module 2014-01-22 17:45:50 +04:00
Roman Arutyunyan fac68de376 recorder now writes correct flv track mask 2014-01-20 19:13:18 +04:00
Roman Arutyunyan 51c1459e04 Merge branch 'envivo-fix' 2014-01-20 09:05:17 +04:00
Roman Arutyunyan 39717828d6 implemented default buflen setting 2014-01-16 22:56:08 +04:00
Roman Arutyunyan a8094dfef1 added clientid to notifications & control 2014-01-16 20:51:33 +04:00
Roman Arutyunyan 8d9f99b540 fixed crash in control drop 2014-01-16 20:35:20 +04:00
Roman Arutyunyan 036a7fd1d5 version up to 1.1.2 2014-01-15 21:06:01 +04:00
Roman Arutyunyan c392252301 updated year 2014-01-15 21:03:53 +04:00
Roman Arutyunyan 65cd61e433 Merge branch 'play2' 2014-01-13 13:50:24 +04:00
Roman Arutyunyan 8a8f28f3b6 changed play2 arg format 2014-01-13 11:35:20 +04:00
Roman Arutyunyan ea1fe6eaf5 changed play2 arg format 2014-01-13 11:31:36 +04:00
Roman Arutyunyan 8e62c72e86 added filename, basename & dirname to exec_record_done 2014-01-12 08:10:36 +04:00
Roman Arutyunyan 8608faad1e version up to 1.1.1 2014-01-10 10:11:29 +04:00
Roman Arutyunyan 5e5dd797c2 reverted changes in live module 2014-01-10 09:19:57 +04:00
Roman Arutyunyan ee55c21b4c Merge branch 'master' into control-redirect 2014-01-10 09:13:35 +04:00
Roman Arutyunyan a8d148473d renamed variable from sun to saun to fix SmarOS compilation 2014-01-08 01:35:40 +04:00
Roman Arutyunyan 8d28f7f1de implemented md5-hashing in notify_relay_redirect mode 2014-01-07 21:07:58 +04:00
Roman Arutyunyan 0166380bef fixed repeated call of on_play_done 2014-01-07 07:27:19 +04:00
Roman Arutyunyan 0aaf2a6259 improved hls fragment boundary condition 2014-01-06 12:17:01 +04:00
Roman Arutyunyan 7dd70f88ea fixed log message 2014-01-06 11:37:30 +04:00
Roman Arutyunyan 5507823b9d fixed crash in hls 2014-01-06 02:06:14 +04:00
Roman Arutyunyan 5c8d5b34fb removed AAC header from HLS handler 2014-01-06 01:54:59 +04:00
Roman Arutyunyan 0a33c0575e fixed HLS stream restart 2014-01-06 00:14:14 +04:00
Roman Arutyunyan 2f6a4fa279 fixed formatting error in stat module 2014-01-05 17:01:50 +04:00
Roman Arutyunyan 13854403e2 Merge branch 'master' into control-redirect 2014-01-03 16:14:03 +04:00
Roman Arutyunyan 5e8fe20497 returned hls discontinuity 2014-01-03 11:31:06 +04:00
Roman Arutyunyan b8b055dd77 implemented async control 2014-01-03 09:30:18 +04:00
Roman Arutyunyan 95075aaa2a fixed parsing addr in control module 2014-01-02 20:05:08 +04:00
Roman Arutyunyan 732b44883c shared fill args func 2014-01-02 18:22:53 +04:00
Roman Arutyunyan f53d158432 implemented play2 2014-01-02 10:22:03 +04:00
Roman Arutyunyan 7e19dce7c4 implemented ordered frames in mp4 vod 2014-01-02 10:20:48 +04:00
Roman Arutyunyan fe0d805179 improved hls boundary check 2014-01-02 10:19:57 +04:00
Roman Arutyunyan ef424df677 added control redirect 2014-01-02 10:19:05 +04:00
Roman Arutyunyan 57a96ac2bc improved Envivio fix: now key length can be unfinalized with object 2014-01-01 13:13:16 +04:00
Roman Arutyunyan 5b68307710 first attempt to implement control redirect 2014-01-01 12:46:51 +04:00
Roman Arutyunyan 307245415b envivo fix; it does not finalize connect() arrays properly 2013-12-29 18:32:04 +04:00
Roman Arutyunyan 095c659ad8 fixed drop/publisher 2013-12-27 18:56:36 +04:00
Roman Arutyunyan f45c0d50db Merge pull request #316 from magicbear/master
add $msec suppoprt for log_format
2013-12-27 03:59:26 -08:00
root 0d1e1fec7c add $msec suppoprt for access_log 2013-12-27 02:44:04 +08:00
Roman Arutyunyan 8542e21e17 up version 2013-12-24 20:09:23 +04:00
Roman Arutyunyan b77337edab Merge branch 'smart-drop' 2013-12-24 20:06:16 +04:00
Roman Arutyunyan bcf7df1e70 fixed crash in dash 2013-12-24 20:04:28 +04:00
Roman Arutyunyan 61cb334917 moved bytes/bw block to the right of stat page 2013-12-23 20:32:33 +04:00
Roman Arutyunyan 6bb620f2fc added audio and video bw to stat 2013-12-23 20:24:40 +04:00
Roman Arutyunyan da0128a2fc fixed windows compilation 2013-12-23 08:06:38 +04:00
Roman Arutyunyan 471d299f5d updated version 2013-12-21 13:57:17 +04:00
Roman Arutyunyan f39d3f66a3 added more data to stat 2013-12-21 12:16:46 +04:00
Roman Arutyunyan 0a5b06609b fixed avc level in stat 2013-12-21 11:19:04 +04:00
Roman Arutyunyan 3a5f9eea78 added aac-he & aac-hev2 support 2013-12-21 09:29:39 +04:00
Roman Arutyunyan 18fa7a5016 implemented avc sps parser & improved dash & stats 2013-12-21 00:47:00 +04:00
Roman Arutyunyan aa4bcf89d7 fixed dash/hls cleanup when recorder{} block exists 2013-12-16 12:58:25 +04:00
Roman Arutyunyan 7eb0f4e2d1 Merge branch 'dash-discont' 2013-12-05 22:46:55 +04:00
Roman Arutyunyan d98b24c4b7 fixed exec_record_done 2013-12-05 22:44:04 +04:00
Roman Arutyunyan e8081fd94f implemented dash discont handling 2013-12-02 20:30:28 +04:00
Roman Arutyunyan b84e00d5bf added cache control for hls & dash 2013-12-02 10:58:58 +04:00
Roman Arutyunyan 9e634ae882 improved mp4 brands 2013-12-01 10:28:21 +04:00
Roman Arutyunyan 51c80e2179 removed TODO 2013-11-30 16:19:30 +04:00
Roman Arutyunyan 31f4788835 updated readme 2013-11-30 11:42:28 +04:00
Roman Arutyunyan fb4793f48a updated readme 2013-11-30 11:41:35 +04:00
Roman Arutyunyan 38126a2c3f added debug log link 2013-11-30 11:40:34 +04:00
Roman Arutyunyan 775f4e0682 added debug log link 2013-11-30 11:39:55 +04:00
Roman Arutyunyan bd73e06884 removed trailing space 2013-11-30 11:35:41 +04:00
Roman Arutyunyan 6f2e105800 update readme 2013-11-30 11:34:48 +04:00
Roman Arutyunyan 4fd9c6c15c split line 2013-11-30 11:33:50 +04:00
Roman Arutyunyan 1b5e19d4e7 updated readme 2013-11-30 11:32:21 +04:00
Roman Arutyunyan 831823a772 removed tabs 2013-11-30 11:24:47 +04:00
Roman Arutyunyan b9fcf7d880 removed trailing spaces 2013-11-30 11:21:53 +04:00
Roman Arutyunyan 96e60a4ac0 fixed dash mp4 format & writer style 2013-11-30 01:38:57 +04:00
Roman Arutyunyan 6e1008ee9c improved dash fragment generated & fixed style 2013-11-29 20:36:11 +04:00
Roman Arutyunyan 047b72c192 added h264 profile/compat/level parser and fixed dash avc1 codec string 2013-11-29 12:55:42 +04:00
Roman Arutyunyan d01ffc0c88 increased version 2013-11-28 22:19:36 +04:00
Roman Arutyunyan 5bd02e7ed3 Merge branch 'dash' 2013-11-28 22:17:17 +04:00
Roman Arutyunyan b07e0e2dee fixed style 2013-11-28 22:08:48 +04:00
Roman Arutyunyan 2f54340454 Merge remote-tracking branch 'ReneVolution/dash' into dash 2013-11-28 21:56:29 +04:00
Roman Arutyunyan 45b39f7762 fixed deleting directory on win 2013-11-28 21:49:15 +04:00
Roman Arutyunyan ed09425a5d test debug logging 2013-11-28 21:40:19 +04:00
Roman Arutyunyan 43ececd070 test debug logging 2013-11-28 21:35:59 +04:00
Roman Arutyunyan 0eb32ae1ca fixing hls & dash cleanup 2013-11-28 21:31:30 +04:00
Roman Arutyunyan d11823bcf8 directory creation in hls module is now performed at publish stage 2013-11-28 21:06:20 +04:00
René Calles 7c7d1f1db2 Merge branch 'dash' of https://github.com/arut/nginx-rtmp-module into dash 2013-11-28 16:41:31 +00:00
Roman Arutyunyan 43718388f4 fixed time formatting & file op error code check 2013-11-28 19:51:15 +04:00
Roman Arutyunyan c928742cb9 fixed file op result code checks 2013-11-28 19:11:31 +04:00
Roman Arutyunyan ec5687f459 improved result code check to work on windows 2013-11-28 19:05:21 +04:00
René Calles 46edc6ccfd Merge branch 'dash' of https://github.com/ReneVolution/nginx-rtmp-module into dash
Conflicts:
	dash/ngx_rtmp_dash_module.c
2013-11-28 15:02:05 +00:00
René Calles 4b3385562a added presentation delay to dash fragments + Repr. IDs 2013-11-28 14:55:43 +00:00
René Calles fb3adeda9f adding some HbbTV compliancy to MPD 2013-11-28 14:55:42 +00:00
Roman Arutyunyan a1b1c205f1 removed extra ngx_de_info call in win32 2013-11-28 18:55:38 +04:00
René Calles 749cea7cb7 fixed representation id naming 2013-11-28 14:53:28 +00:00
Roman Arutyunyan 9abf8355d9 removed old variable 2013-11-28 18:21:08 +04:00
Roman Arutyunyan fcbd18c7f7 fixed windows compilation 2013-11-28 17:26:06 +04:00
Roman Arutyunyan 29dfda882a fixed using raw non-null-terminated strings from config parser in syscalls 2013-11-28 17:14:39 +04:00
Roman Arutyunyan 27df4c10af updated prject title 2013-11-28 14:20:09 +04:00
Roman Arutyunyan b73075e28c added dash_nested, added directory creation, fixed init frags cleanup 2013-11-28 14:14:40 +04:00
René Calles ed68e4a2eb added presentation delay to dash fragments + Repr. IDs 2013-11-27 17:04:09 +00:00
Roman Arutyunyan 15ec66a7bf added presentation delay to dash fragments 2013-11-26 17:13:38 +04:00
Roman Arutyunyan 995688b9cf enotify stuff is now in exec module 2013-11-26 14:13:33 +04:00
Roman Arutyunyan a933495bf1 merged exec pull feature 2013-11-26 14:13:07 +04:00
Roman Arutyunyan 935156c352 fixed formatting 2013-11-25 20:15:10 +04:00
René Calles ba94a0c1e4 adding some HbbTV compliancy to MPD 2013-11-25 11:01:07 +00:00
René Calles 33240c33df fixed representation id naming 2013-11-25 09:59:04 +00:00
Roman Arutyunyan f7d65ba915 added bandwith to mpd playlist 2013-11-25 12:50:05 +04:00
René Calles a95065b90d fixed bandwith representation 2013-11-23 16:19:41 +00:00
René Calles 9227aca90d fixed times 2013-11-23 13:42:45 +00:00
Roman Arutyunyan 884366a2e0 fixed times 2013-11-23 15:16:04 +04:00
azure provisioned user f6af3f2343 fixed MPD Validation issue 2013-11-23 10:34:10 +00:00
Roman Arutyunyan 24addede15 improved dash timing 2013-11-21 11:55:07 +04:00
Roman Arutyunyan 85dbf397e4 added bound checks & simplified box creation 2013-11-21 11:39:04 +04:00
Roman Arutyunyan 81ce4788f9 optimized for dash.js live 2013-11-21 03:13:16 +04:00
Roman Arutyunyan 20584b05b8 added reconnect to rtmp player light 2013-11-20 21:20:51 +04:00
Roman Arutyunyan bec6d8553a preserve dash init files from cleanup 2013-11-20 21:19:30 +04:00
Roman Arutyunyan 1d421c85f4 fixed idle_stream off 2013-11-20 17:12:35 +04:00
Roman Arutyunyan 87ee276489 added light (buttonless) version of player 2013-11-20 11:15:35 +04:00
Roman Arutyunyan a9ddfed095 reduced buffering time 2013-11-12 22:22:50 +04:00
Roman Arutyunyan 0dca45f0b6 fixed dash fragment cleanup 2013-11-12 22:00:07 +04:00
Roman Arutyunyan 8c0b83514c added buffering based on fraglen 2013-11-12 21:45:54 +04:00
Roman Arutyunyan 2c7111cf10 fixed opening fragments & improved style 2013-11-12 21:34:10 +04:00
Roman Arutyunyan 3b680915a8 implemented live presentation offset 2013-11-12 20:53:54 +04:00
Roman Arutyunyan df33838e37 reimplemented fragments 2013-11-12 20:42:29 +04:00
Roman Arutyunyan fe4877f216 implemented timeline dash 2013-11-12 19:48:49 +04:00
Roman Arutyunyan 37033ed8c0 added bound checks & simplified box creation 2013-11-12 18:56:30 +04:00
Roman Arutyunyan 85d34da0d4 style fixes 2013-11-12 17:38:47 +04:00
Roman Arutyunyan c18fd3aad4 video & audio working 2013-11-12 17:20:16 +04:00
Roman Arutyunyan 6caee7cb88 working video 2013-11-12 14:38:08 +04:00
Roman Arutyunyan 0e07492797 fixed sample sizes in dash 2013-11-11 13:57:02 +04:00
Roman Arutyunyan 7ea7690db2 refactored dash & fixed style 2013-11-10 23:24:31 +04:00
Roman Arutyunyan 6067ac21e0 Merge remote-tracking branch 'stephenbasile/dash' into dash 2013-11-10 09:31:31 +04:00
Roman Arutyunyan 748192877a added RIPN whois link to stat.xsl 2013-11-08 22:11:31 +04:00
Roman Arutyunyan 5a38108bad updated copyright 2013-11-07 19:28:19 +04:00
Roman Arutyunyan 503375fea8 updated copyright 2013-11-07 19:27:49 +04:00
Roman Arutyunyan 0fb3ce40e0 implemented idle_streams feature 2013-11-07 19:14:01 +04:00
Roman Arutyunyan 8f9965b7f2 added nbsp to stat.xsl to delimit versions 2013-11-06 19:13:25 +04:00
Roman Arutyunyan 91c69aed28 fixed stream sync in wait_video mode 2013-11-06 12:24:03 +04:00
Roman Arutyunyan 401d7e929c added version file to deps 2013-11-04 01:02:59 +04:00
Roman Arutyunyan 1cfb7aeb58 updated version 2013-11-04 01:00:31 +04:00
Roman Arutyunyan b564a096fd Merge branch 'peer-timestamp' 2013-11-03 23:37:38 +04:00
Roman Arutyunyan 8856d11dd9 added nginx/module version to xml & xsl 2013-11-03 23:29:00 +04:00
Roman Arutyunyan f599c70568 updated copyright line 2013-11-03 23:11:37 +04:00
Roman Arutyunyan a6913ea68a fixed path and recorder evaluation in execs 2013-10-27 18:12:46 +04:00
Roman Arutyunyan 893e17343a reimplemented execs; merged managed & unmanaged execs 2013-10-22 15:20:06 +04:00
Stephen Basile 040c96ed9b moved dash to its own folder 2013-10-20 14:36:28 -04:00
Stephen Basile 9177be55dd MPD tweaks and stubbed out MP3 support 2013-10-09 16:12:47 -04:00
Roman Arutyunyan b62651efbd improved logging 2013-10-09 12:21:33 +04:00
Stephen Basile 055228cc8a style changes 2013-10-08 16:15:44 -04:00
Stephen Basile 0ac369cd5a Explict cast on MP4 writer fuctions 2013-10-08 15:30:55 -04:00
Roman Arutyunyan 2f3ff168c3 added mp4a version 1 & 2 support to mp4 vod 2013-10-08 15:23:29 +04:00
Roman Arutyunyan c89459b784 style fix 2013-10-08 02:21:56 +04:00
Roman Arutyunyan f8588a450f fixed typo 2013-10-08 02:20:47 +04:00
Roman Arutyunyan 97d144ae28 implemented exec_pull feature; the old exec is now exec_push 2013-10-08 02:04:49 +04:00
Roman Arutyunyan 737ea5ce34 implemented HLS EVENT playlist type 2013-10-05 18:17:15 +04:00
Stephen Basile 05548c515f added the presentation time offset back in 2013-10-01 10:31:02 -04:00
Stephen Basile 857eca9a61 fix precision on duration 2013-09-30 17:56:37 -04:00
Stephen Basile 2060dccbf6 Better support for variable keyframe intervals 2013-09-30 16:27:31 -04:00
Stephen Basile a5a59aa50c MPEG-DASH Support
Please not this is a work in progress.  For testing, please set dash_cleanup off;.
You can use this player to test.  http://dashif.org/reference/players/javascript/1.0.0/index.html
If you have a specific player that you need changes to support, please file an issue on github
2013-09-30 13:58:02 -04:00
Roman Arutyunyan 940ff26009 added current timestamp to stat and on_update 2013-09-19 18:08:17 +04:00
Roman Arutyunyan 67443c28b4 added timestamp to meta-copy packet 2013-09-18 16:55:19 +04:00
Roman Arutyunyan f052e4cc48 fixed repeated onMetaData string 2013-09-18 12:25:15 +04:00
Roman Arutyunyan 0f8641d0c8 implemented meta copy 2013-09-17 16:36:12 +04:00
Roman Arutyunyan fa2b5b037f added _definst_ support for better Wowza compliance 2013-09-05 16:45:44 +04:00
Roman Arutyunyan 5eb6dce453 added escaping address in stat xml 2013-08-30 07:50:28 +04:00
Roman Arutyunyan face62aa34 fixed hls urls for nested case 2013-08-28 23:42:40 +04:00
Roman Arutyunyan 49a3ee0dc6 implemented HLS base urls 2013-08-27 20:22:24 +04:00
Roman Arutyunyan db3fc521d8 added 1 sec buffering to rtmp player 2013-08-22 16:17:12 +04:00
Roman Arutyunyan c442e1fc2d changed mx VideoDisplay to Spark VideoDisplay in RtmpPublisher 2013-08-20 20:47:53 +04:00
Roman Arutyunyan b4e77fa750 changed mx VideoDisplay to Spark VideoDisplay in RtmpPlayer 2013-08-20 18:13:28 +04:00
Roman Arutyunyan 18e4762db2 improved dropper in control module; now dropper support dropping all clients with the same address; dropping all subscribers is supported as well 2012-11-29 23:28:56 +04:00
80 changed files with 12949 additions and 2877 deletions

5
.gitignore vendored Normal file
View file

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

View file

@ -6,3 +6,10 @@ Project author:
Contacts:
arut@qip.ru
arutyunyan.roman@gmail.com
Fork author:
Sergey Dryanzhinsky
Moscow, Russia
Contacts:
sergey.dryabzhinsky@gmail.com

View file

@ -1,14 +1,14 @@
Copyright (c) 2012-2013, Roman Arutyunyan
Copyright (c) 2012-2014, Roman Arutyunyan
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED

118
README.md
View file

@ -1,14 +1,29 @@
# NGINX-based RTMP server
## nginx-rtmp-module
# NGINX-based Media Streaming Server
## 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
@ -22,9 +37,9 @@
### Features
* Live streaming of video/audio
* RTMP/HLS/MPEG-DASH live streaming
* Video on demand FLV/MP4,
* RTMP Video on demand FLV/MP4,
playing from local filesystem or HTTP
* Stream relay support for distributed
@ -36,8 +51,6 @@
* Online transcoding with FFmpeg
* HLS (HTTP Live Streaming) support
* HTTP callbacks (publish/play/record/update etc)
* Running external programs on certain events (exec)
@ -49,9 +62,9 @@
level for faster streaming and low
memory footprint
* Proved to work with Wirecast,FMS,Wowza,
JWPlayer,FlowPlayer,StrobeMediaPlayback,
ffmpeg,avconv,rtmpdump,flvstreamer
* Proved to work with Wirecast, FMS, Wowza,
JWPlayer, FlowPlayer, StrobeMediaPlayback,
ffmpeg, avconv, rtmpdump, flvstreamer
and many more
* Statistics in XML/XSL in machine- & human-
@ -63,14 +76,26 @@
cd to NGINX source directory & run this:
./configure --add-module=<path-to-nginx-rtmp-module>
./configure --add-module=/path/to/nginx-rtmp-module
make
make install
Several versions of nginx (1.3.14 - 1.5.0) require http_ssl_module to be
added as well:
./configure --add-module=<path-to-nginx-rtmp-module> --with-http_ssl_module
./configure --add-module=/path/to/nginx-rtmp-module --with-http_ssl_module
For building debug version of nginx add `--with-debug`
./configure --add-module=/path/to-nginx/rtmp-module --with-debug
[Read more about debug log](https://github.com/arut/nginx-rtmp-module/wiki/Debug-log)
### Contributing and Branch Policy
The "dev" branch is the one where all contributions will be merged before reaching "master".
If you plan to propose a patch, please commit into the "dev" branch or its own feature branch.
Direct commit to "master" are not permitted.
### Windows limitations
@ -93,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
@ -134,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 &
@ -146,7 +169,8 @@ rtmp_auto_push directive.
#
# Multiple exec lines can be specified.
exec ffmpeg -re -i rtmp://localhost:1935/$app/$name -vcodec flv -acodec copy -s 32x32 -f flv rtmp://localhost:1935/small/${name};
exec ffmpeg -re -i rtmp://localhost:1935/$app/$name -vcodec flv -acodec copy -s 32x32
-f flv rtmp://localhost:1935/small/${name};
}
application small {
@ -158,14 +182,15 @@ rtmp_auto_push directive.
live on;
# Stream from local webcam
exec_static ffmpeg -f video4linux2 -i /dev/video0 -c:v libx264 -an -f flv rtmp://localhost:1935/webcam/mystream;
exec_static ffmpeg -f video4linux2 -i /dev/video0 -c:v libx264 -an
-f flv rtmp://localhost:1935/webcam/mystream;
}
application mypush {
live on;
# Every stream published here
# is automatically pushed to
# is automatically pushed to
# these two machines
push rtmp1.example.com;
push rtmp2.example.com:1934;
@ -178,7 +203,7 @@ rtmp_auto_push directive.
# and play locally
pull rtmp://rtmp3.example.com pageUrl=www.example.com/index.html;
}
application mystaticpull {
live on;
@ -201,8 +226,8 @@ rtmp_auto_push directive.
live on;
# The following notifications receive all
# the session variables as well as
# The following notifications receive all
# the session variables as well as
# particular call arguments in HTTP POST
# request
@ -218,7 +243,7 @@ rtmp_auto_push directive.
on_done http://localhost:8080/done;
# All above mentioned notifications receive
# standard connect() arguments as well as
# standard connect() arguments as well as
# play/publish ones. If any arguments are sent
# with GET-style syntax to play & publish
# these are also included.
@ -239,7 +264,7 @@ rtmp_auto_push directive.
# HLS
# For HLS to work please create a directory in tmpfs (/tmp/app here)
# For HLS to work please create a directory in tmpfs (/tmp/hls here)
# for the fragments. The directory contents is served via HTTP (see
# http{} section in config)
#
@ -247,8 +272,8 @@ rtmp_auto_push directive.
# profile (see ffmpeg example).
# This example creates RTMP stream from movie ready for HLS:
#
# ffmpeg -loglevel verbose -re -i movie.avi -vcodec libx264
# -vprofile baseline -acodec libmp3lame -ar 44100 -ac 1
# ffmpeg -loglevel verbose -re -i movie.avi -vcodec libx264
# -vprofile baseline -acodec libmp3lame -ar 44100 -ac 1
# -f flv rtmp://localhost:1935/hls/movie
#
# If you need to transcode live stream use 'exec' feature.
@ -256,10 +281,16 @@ rtmp_auto_push directive.
application hls {
live on;
hls on;
hls_path /tmp/app;
hls_fragment 5s;
hls_path /tmp/hls;
}
# MPEG-DASH is similar to HLS
application dash {
live on;
dash on;
dash_path /tmp/dash;
}
}
}
@ -292,29 +323,14 @@ rtmp_auto_push directive.
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
alias /tmp/app;
expires -1;
root /tmp;
add_header Cache-Control no-cache;
}
}
}
### Multi-worker streaming example
rtmp_auto_push on;
rtmp {
server {
listen 1935;
chunk_size 4000;
# TV mode: one publisher, many subscribers
application mytv {
live on;
location /dash {
# Serve DASH fragments
root /tmp;
add_header Cache-Control no-cache;
}
}
}

5
TODO
View file

@ -1,5 +0,0 @@
- mpeg-dash
- akamai auth
- manual recorder custom file name
- bandwidth control (per-app & total)
- multiple streams per connection

69
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,38 +14,37 @@ CORE_MODULES="$CORE_MODULES
ngx_rtmp_relay_module \
ngx_rtmp_exec_module \
ngx_rtmp_auto_push_module \
ngx_rtmp_enotify_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 \
$ngx_addon_dir/ngx_rtmp_codec_module.h \
$ngx_addon_dir/ngx_rtmp_eval.h \
$ngx_addon_dir/ngx_rtmp.h \
$ngx_addon_dir/ngx_rtmp_version.h \
$ngx_addon_dir/ngx_rtmp_live_module.h \
$ngx_addon_dir/ngx_rtmp_netcall_module.h \
$ngx_addon_dir/ngx_rtmp_play_module.h \
$ngx_addon_dir/ngx_rtmp_record_module.h \
$ngx_addon_dir/ngx_rtmp_relay_module.h \
$ngx_addon_dir/ngx_rtmp_streams.h \
$ngx_addon_dir/ngx_rtmp_bitop.h \
$ngx_addon_dir/ngx_rtmp_proxy_protocol.h \
$ngx_addon_dir/hls/ngx_rtmp_mpegts.h \
$ngx_addon_dir/dash/ngx_rtmp_mp4.h \
"
NGX_ADDON_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 \
@ -66,20 +64,55 @@ 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_enotify_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 \
$ngx_addon_dir/ngx_rtmp_proxy_protocol.c \
$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 \
"
CFLAGS="$CFLAGS -I$ngx_addon_dir"
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"

1761
dash/ngx_rtmp_dash_module.c Normal file

File diff suppressed because it is too large Load diff

1167
dash/ngx_rtmp_mp4.c Normal file

File diff suppressed because it is too large Load diff

52
dash/ngx_rtmp_mp4.h Normal file
View file

@ -0,0 +1,52 @@
#ifndef _NGX_RTMP_MP4_H_INCLUDED_
#define _NGX_RTMP_MP4_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_rtmp.h>
#define NGX_RTMP_MP4_SAMPLE_SIZE 0x01
#define NGX_RTMP_MP4_SAMPLE_DURATION 0x02
#define NGX_RTMP_MP4_SAMPLE_DELAY 0x04
#define NGX_RTMP_MP4_SAMPLE_KEY 0x08
typedef struct {
uint32_t size;
uint32_t duration;
uint32_t delay;
uint32_t timestamp;
unsigned key:1;
} ngx_rtmp_mp4_sample_t;
typedef enum {
NGX_RTMP_MP4_FILETYPE_INIT,
NGX_RTMP_MP4_FILETYPE_SEG
} ngx_rtmp_mp4_file_type_t;
typedef enum {
NGX_RTMP_MP4_VIDEO_TRACK,
NGX_RTMP_MP4_AUDIO_TRACK
} ngx_rtmp_mp4_track_type_t;
ngx_int_t ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b);
ngx_int_t ngx_rtmp_mp4_write_styp(ngx_buf_t *b);
ngx_int_t ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b,
ngx_rtmp_mp4_track_type_t ttype);
ngx_int_t ngx_rtmp_mp4_write_moof(ngx_buf_t *b, uint32_t earliest_pres_time,
uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples,
ngx_uint_t sample_mask, uint32_t index);
ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_buf_t *b,
ngx_uint_t reference_size, uint32_t earliest_pres_time,
uint32_t latest_pres_time);
ngx_uint_t ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size);
#endif /* _NGX_RTMP_MP4_H_INCLUDED_ */

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

@ -1 +0,0 @@
HLS is now a part of rtmp module

File diff suppressed because it is too large Load diff

View file

@ -1,23 +1,36 @@
/*
* Copyright (c) 2013 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
#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,
@ -36,20 +49,28 @@ static u_char ngx_rtmp_mpegts_header[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
/* 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)
/* TS */
0x47, 0x50, 0x01, 0x10, 0x00,
/* PSI */
0x02, 0xb0, 0x17, 0x00, 0x01, 0xc1, 0x00, 0x00,
/* PMT */
0xe1, 0x00,
0xf0, 0x00,
0x1b, 0xe1, 0x00, 0xf0, 0x00, /* h264 */
0x0f, 0xe1, 0x01, 0xf0, 0x00, /* aac */
/*0x03, 0xe1, 0x01, 0xf0, 0x00,*/ /* mp3 */
/* CRC */
0x2f, 0x44, 0xb9, 0x9b, /* crc for aac */
/*0x4e, 0x59, 0x3d, 0x1e,*/ /* crc for mp3 */
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,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
@ -69,20 +90,164 @@ 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[] = {
//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[] = {
//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
ngx_int_t
ngx_rtmp_mpegts_write_header(ngx_file_t *file)
static ngx_int_t
ngx_rtmp_mpegts_write_file(ngx_rtmp_mpegts_file_t *file, u_char *in,
size_t in_size)
{
ssize_t rc;
u_char *out;
size_t out_size, n;
ssize_t rc;
rc = ngx_write_file(file, ngx_rtmp_mpegts_header,
sizeof(ngx_rtmp_mpegts_header), 0);
static u_char buf[1024];
return rc > 0 ? NGX_OK : rc;
if (!file->encrypt) {
ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
"mpegts: write %uz bytes", in_size);
rc = ngx_write_fd(file->fd, in, in_size);
if (rc < 0) {
return NGX_ERROR;
}
return NGX_OK;
}
/* encrypt */
ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
"mpegts: write %uz encrypted bytes", in_size);
out = buf;
out_size = sizeof(buf);
if (file->size > 0 && file->size + in_size >= 16) {
ngx_memcpy(file->buf + file->size, in, 16 - file->size);
in += 16 - file->size;
in_size -= 16 - file->size;
AES_cbc_encrypt(file->buf, out, 16, &file->key, file->iv, AES_ENCRYPT);
out += 16;
out_size -= 16;
file->size = 0;
}
for ( ;; ) {
n = in_size & ~0x0f;
if (n > 0) {
if (n > out_size) {
n = out_size;
}
AES_cbc_encrypt(in, out, n, &file->key, file->iv, AES_ENCRYPT);
in += n;
in_size -= n;
} else if (out == buf) {
break;
}
rc = ngx_write_fd(file->fd, buf, out - buf + n);
if (rc < 0) {
return NGX_ERROR;
}
out = buf;
out_size = sizeof(buf);
}
if (in_size) {
ngx_memcpy(file->buf + file->size, in, in_size);
file->size += in_size;
}
return NGX_OK;
}
static ngx_int_t
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
//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;
}
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));
}
@ -121,17 +286,17 @@ ngx_rtmp_mpegts_write_pts(u_char *p, ngx_uint_t fb, uint64_t pts)
ngx_int_t
ngx_rtmp_mpegts_write_frame(ngx_file_t *file, ngx_rtmp_mpegts_frame_t *f,
ngx_buf_t *b)
ngx_rtmp_mpegts_write_frame(ngx_rtmp_mpegts_file_t *file,
ngx_rtmp_mpegts_frame_t *f, ngx_buf_t *b)
{
ngx_uint_t pes_size, header_size, body_size, in_size, stuff_size, flags;
u_char packet[188], *p, *base;
ngx_int_t first, rc;
ngx_log_debug6(NGX_LOG_DEBUG_HTTP, file->log, 0,
ngx_log_debug6(NGX_LOG_DEBUG_CORE, file->log, 0,
"mpegts: pid=%ui, sid=%ui, pts=%uL, "
"dts=%uL, key=%ui, size=%ui",
f->pid, f->sid, f->pts, f->dts,
f->pid, f->sid, f->pts, f->dts,
(ngx_uint_t) f->key, (size_t) (b->last - b->pos));
first = 1;
@ -153,14 +318,12 @@ ngx_rtmp_mpegts_write_frame(ngx_file_t *file, ngx_rtmp_mpegts_frame_t *f,
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 */
@ -188,7 +351,7 @@ ngx_rtmp_mpegts_write_frame(ngx_file_t *file, ngx_rtmp_mpegts_frame_t *f,
*p++ = (u_char) flags;
*p++ = (u_char) header_size;
p = ngx_rtmp_mpegts_write_pts(p, flags >> 6, f->pts +
p = ngx_rtmp_mpegts_write_pts(p, flags >> 6, f->pts +
NGX_RTMP_HLS_DELAY);
if (f->dts != f->pts) {
@ -237,11 +400,87 @@ ngx_rtmp_mpegts_write_frame(ngx_file_t *file, ngx_rtmp_mpegts_frame_t *f,
b->pos = b->last;
}
rc = ngx_write_file(file, packet, sizeof(packet), file->offset);
if (rc < 0) {
rc = ngx_rtmp_mpegts_write_file(file, packet, sizeof(packet));
if (rc != NGX_OK) {
return rc;
}
}
return NGX_OK;
}
ngx_int_t
ngx_rtmp_mpegts_init_encryption(ngx_rtmp_mpegts_file_t *file,
u_char *key, size_t key_len, uint64_t iv)
{
if (AES_set_encrypt_key(key, key_len * 8, &file->key)) {
return NGX_ERROR;
}
ngx_memzero(file->iv, 8);
file->iv[8] = (u_char) (iv >> 56);
file->iv[9] = (u_char) (iv >> 48);
file->iv[10] = (u_char) (iv >> 40);
file->iv[11] = (u_char) (iv >> 32);
file->iv[12] = (u_char) (iv >> 24);
file->iv[13] = (u_char) (iv >> 16);
file->iv[14] = (u_char) (iv >> 8);
file->iv[15] = (u_char) (iv);
file->encrypt = 1;
return NGX_OK;
}
ngx_int_t
ngx_rtmp_mpegts_open_file(ngx_rtmp_mpegts_file_t *file, u_char *path,
ngx_log_t *log, ngx_rtmp_codec_ctx_t *codec_ctx, ngx_uint_t mpegts_cc)
{
file->log = log;
file->fd = ngx_open_file(path, NGX_FILE_WRONLY, NGX_FILE_TRUNCATE,
NGX_FILE_DEFAULT_ACCESS);
if (file->fd == NGX_INVALID_FILE) {
ngx_log_error(NGX_LOG_ERR, log, ngx_errno,
"hls: error creating fragment file");
return NGX_ERROR;
}
file->size = 0;
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);
return NGX_ERROR;
}
return NGX_OK;
}
ngx_int_t
ngx_rtmp_mpegts_close_file(ngx_rtmp_mpegts_file_t *file)
{
u_char buf[16];
ssize_t rc;
if (file->encrypt) {
ngx_memset(file->buf + file->size, 16 - file->size, 16 - file->size);
AES_cbc_encrypt(file->buf, buf, 16, &file->key, file->iv, AES_ENCRYPT);
rc = ngx_write_fd(file->fd, buf, 16);
if (rc < 0) {
return NGX_ERROR;
}
}
ngx_close_file(file->fd);
return NGX_OK;
}

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2013 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -9,6 +10,20 @@
#include <ngx_config.h>
#include <ngx_core.h>
#include <openssl/aes.h>
#include <ngx_rtmp_codec_module.h>
typedef struct {
ngx_fd_t fd;
ngx_log_t *log;
unsigned encrypt:1;
unsigned size:4;
u_char buf[16];
u_char iv[16];
AES_KEY key;
} ngx_rtmp_mpegts_file_t;
typedef struct {
@ -21,9 +36,13 @@ typedef struct {
} ngx_rtmp_mpegts_frame_t;
ngx_int_t ngx_rtmp_mpegts_write_header(ngx_file_t *file);
ngx_int_t ngx_rtmp_mpegts_write_frame(ngx_file_t *file,
ngx_rtmp_mpegts_frame_t *f, ngx_buf_t *b);
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_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);
#endif /* _NGX_RTMP_MPEGTS_H_INCLUDED_ */

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

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -21,16 +22,23 @@ static ngx_int_t ngx_rtmp_add_addrs6(ngx_conf_t *cf, ngx_rtmp_port_t *mport,
ngx_rtmp_conf_addr_t *addr);
#endif
static ngx_int_t ngx_rtmp_cmp_conf_addrs(const void *one, const void *two);
static ngx_int_t ngx_rtmp_init_events(ngx_conf_t *cf,
static ngx_int_t ngx_rtmp_init_events(ngx_conf_t *cf,
ngx_rtmp_core_main_conf_t *cmcf);
static ngx_int_t ngx_rtmp_init_event_handlers(ngx_conf_t *cf,
static ngx_int_t ngx_rtmp_init_event_handlers(ngx_conf_t *cf,
ngx_rtmp_core_main_conf_t *cmcf);
static char * ngx_rtmp_merge_applications(ngx_conf_t *cf,
ngx_array_t *applications, void **app_conf, ngx_rtmp_module_t *module,
static char * ngx_rtmp_merge_applications(ngx_conf_t *cf,
ngx_array_t *applications, void **app_conf, ngx_rtmp_module_t *module,
ngx_uint_t ctx_index);
static ngx_int_t ngx_rtmp_init_process(ngx_cycle_t *cycle);
#if (nginx_version >= 1007011)
ngx_queue_t ngx_rtmp_init_queue;
#elif (nginx_version >= 1007005)
ngx_thread_volatile ngx_queue_t ngx_rtmp_init_queue;
#else
ngx_thread_volatile ngx_event_t *ngx_rtmp_init_queue;
#endif
ngx_uint_t ngx_rtmp_max_module;
@ -63,7 +71,7 @@ ngx_module_t ngx_rtmp_module = {
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
ngx_rtmp_init_process, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
@ -79,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;
@ -93,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++;
}
@ -140,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);
@ -173,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) {
@ -204,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 */
@ -246,7 +259,7 @@ ngx_rtmp_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
/*ctx->app_conf = cscfp[s]->ctx->loc_conf;*/
rv = module->merge_app_conf(cf,
rv = module->merge_app_conf(cf,
ctx->app_conf[mi],
cscfp[s]->ctx->app_conf[mi]);
if (rv != NGX_CONF_OK) {
@ -275,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) {
@ -342,7 +355,7 @@ ngx_rtmp_merge_applications(ngx_conf_t *cf, ngx_array_t *applications,
}
cacf = (*cacfp)->app_conf[ngx_rtmp_core_module.ctx_index];
rv = ngx_rtmp_merge_applications(cf, &cacf->applications,
rv = ngx_rtmp_merge_applications(cf, &cacf->applications,
(*cacfp)->app_conf,
module, ctx_index);
if (rv != NGX_CONF_OK) {
@ -362,14 +375,14 @@ ngx_rtmp_init_events(ngx_conf_t *cf, ngx_rtmp_core_main_conf_t *cmcf)
size_t n;
for(n = 0; n < NGX_RTMP_MAX_EVENT; ++n) {
if (ngx_array_init(&cmcf->events[n], cf->pool, 1,
if (ngx_array_init(&cmcf->events[n], cf->pool, 1,
sizeof(ngx_rtmp_handler_pt)) != NGX_OK)
{
return NGX_ERROR;
}
}
if (ngx_array_init(&cmcf->amf, cf->pool, 1,
if (ngx_array_init(&cmcf->amf, cf->pool, 1,
sizeof(ngx_rtmp_amf_handler_t)) != NGX_OK)
{
return NGX_ERROR;
@ -432,7 +445,7 @@ ngx_rtmp_init_event_handlers(ngx_conf_t *cf, ngx_rtmp_core_main_conf_t *cmcf)
for(n = 0; n < cmcf->amf.nelts; ++n, ++h) {
ha = cmcf->amf_arrays.elts;
for(m = 0; m < cmcf->amf_arrays.nelts; ++m, ++ha) {
if (h->name.len == ha->key.len
if (h->name.len == ha->key.len
&& !ngx_strncmp(h->name.data, ha->key.data, ha->key.len))
{
break;
@ -442,7 +455,7 @@ ngx_rtmp_init_event_handlers(ngx_conf_t *cf, ngx_rtmp_core_main_conf_t *cmcf)
ha = ngx_array_push(&cmcf->amf_arrays);
ha->key = h->name;
ha->key_hash = ngx_hash_key_lc(ha->key.data, ha->key.len);
ha->value = ngx_array_create(cf->pool, 1,
ha->value = ngx_array_create(cf->pool, 1,
sizeof(ngx_rtmp_handler_pt));
if (ha->value == NULL) {
return NGX_ERROR;
@ -543,6 +556,7 @@ found:
addr->bind = listen->bind;
addr->wildcard = listen->wildcard;
addr->so_keepalive = listen->so_keepalive;
addr->proxy_protocol = listen->proxy_protocol;
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
addr->tcp_keepidle = listen->tcp_keepidle;
addr->tcp_keepintvl = listen->tcp_keepintvl;
@ -701,6 +715,7 @@ ngx_rtmp_add_addrs(ngx_conf_t *cf, ngx_rtmp_port_t *mport,
addrs[i].conf.addr_text.len = len;
addrs[i].conf.addr_text.data = p;
addrs[i].conf.proxy_protocol = addr->proxy_protocol;
}
return NGX_OK;
@ -750,6 +765,7 @@ ngx_rtmp_add_addrs6(ngx_conf_t *cf, ngx_rtmp_port_t *mport,
addrs6[i].conf.addr_text.len = len;
addrs6[i].conf.addr_text.data = p;
addrs6[i].conf.proxy_protocol = addr->proxy_protocol;
}
return NGX_OK;
@ -823,3 +839,13 @@ ngx_rtmp_rmemcpy(void *dst, const void* src, size_t n)
return dst;
}
static ngx_int_t
ngx_rtmp_init_process(ngx_cycle_t *cycle)
{
#if (nginx_version >= 1007005)
ngx_queue_init((ngx_queue_t*) &ngx_rtmp_init_queue);
#endif
return NGX_OK;
}

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -11,6 +12,7 @@
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_event_connect.h>
#include <nginx.h>
#include "ngx_rtmp_amf.h"
#include "ngx_rtmp_bandwidth.h"
@ -42,6 +44,7 @@ typedef struct {
unsigned ipv6only:2;
#endif
unsigned so_keepalive:2;
unsigned proxy_protocol:1;
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
int tcp_keepidle;
int tcp_keepintvl;
@ -53,19 +56,20 @@ typedef struct {
typedef struct {
ngx_rtmp_conf_ctx_t *ctx;
ngx_str_t addr_text;
unsigned proxy_protocol:1;
} ngx_rtmp_addr_conf_t;
typedef struct {
in_addr_t addr;
ngx_rtmp_addr_conf_t conf;
in_addr_t addr;
} ngx_rtmp_in_addr_t;
#if (NGX_HAVE_INET6)
typedef struct {
struct in6_addr addr6;
ngx_rtmp_addr_conf_t conf;
struct in6_addr addr6;
} ngx_rtmp_in6_addr_t;
#endif
@ -96,6 +100,7 @@ typedef struct {
unsigned ipv6only:2;
#endif
unsigned so_keepalive:2;
unsigned proxy_protocol:1;
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
int tcp_keepidle;
int tcp_keepintvl;
@ -194,7 +199,11 @@ typedef struct {
ngx_str_t *addr_text;
int connected;
#if (nginx_version >= 1007005)
ngx_queue_t posted_dry_events;
#else
ngx_event_t *posted_dry_events;
#endif
/* client buffer time in msec */
uint32_t buflen;
@ -220,6 +229,10 @@ typedef struct {
ngx_msec_t epoch;
ngx_msec_t peer_epoch;
ngx_msec_t base_time;
uint32_t current_time;
/* ready for publishing? */
unsigned ready_for_publish:1;
/* ping */
ngx_event_t ping_evt;
@ -305,7 +318,7 @@ typedef struct ngx_rtmp_core_srv_conf_s {
ngx_int_t max_streams;
ngx_uint_t ack_window;
ngx_int_t chunk_size;
ngx_pool_t *pool;
ngx_chain_t *free;
@ -316,6 +329,7 @@ typedef struct ngx_rtmp_core_srv_conf_s {
ngx_flag_t busy;
size_t out_queue;
size_t out_cork;
ngx_msec_t buflen;
ngx_rtmp_conf_ctx_t *ctx;
} ngx_rtmp_core_srv_conf_t;
@ -342,7 +356,7 @@ typedef struct {
char *(*init_main_conf)(ngx_conf_t *cf, void *conf);
void *(*create_srv_conf)(ngx_conf_t *cf);
char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev,
char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev,
void *conf);
void *(*create_app_conf)(ngx_conf_t *cf);
@ -388,7 +402,7 @@ char* ngx_rtmp_user_message_type(uint16_t evt);
#endif
void ngx_rtmp_init_connection(ngx_connection_t *c);
ngx_rtmp_session_t * ngx_rtmp_init_session(ngx_connection_t *c,
ngx_rtmp_session_t * ngx_rtmp_init_session(ngx_connection_t *c,
ngx_rtmp_addr_conf_t *addr_conf);
void ngx_rtmp_finalize_session(ngx_rtmp_session_t *s);
void ngx_rtmp_handshake(ngx_rtmp_session_t *s);
@ -467,9 +481,9 @@ ngx_int_t ngx_rtmp_amf_shared_object_handler(ngx_rtmp_session_t *s,
--ngx_rtmp_ref(b)
ngx_chain_t * ngx_rtmp_alloc_shared_buf(ngx_rtmp_core_srv_conf_t *cscf);
void ngx_rtmp_free_shared_chain(ngx_rtmp_core_srv_conf_t *cscf,
void ngx_rtmp_free_shared_chain(ngx_rtmp_core_srv_conf_t *cscf,
ngx_chain_t *in);
ngx_chain_t * ngx_rtmp_append_shared_bufs(ngx_rtmp_core_srv_conf_t *cscf,
ngx_chain_t * ngx_rtmp_append_shared_bufs(ngx_rtmp_core_srv_conf_t *cscf,
ngx_chain_t *head, ngx_chain_t *in);
#define ngx_rtmp_acquire_shared_chain(in) \
@ -477,7 +491,7 @@ ngx_chain_t * ngx_rtmp_append_shared_bufs(ngx_rtmp_core_srv_conf_t *cscf,
/* Sending messages */
void ngx_rtmp_prepare_message(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
void ngx_rtmp_prepare_message(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_rtmp_header_t *lh, ngx_chain_t *out);
ngx_int_t ngx_rtmp_send_message(ngx_rtmp_session_t *s, ngx_chain_t *out,
ngx_uint_t priority);
@ -492,64 +506,64 @@ ngx_int_t ngx_rtmp_send_message(ngx_rtmp_session_t *s, ngx_chain_t *out,
#define NGX_RTMP_LIMIT_DYNAMIC 2
/* Protocol control messages */
ngx_chain_t * ngx_rtmp_create_chunk_size(ngx_rtmp_session_t *s,
ngx_chain_t * ngx_rtmp_create_chunk_size(ngx_rtmp_session_t *s,
uint32_t chunk_size);
ngx_chain_t * ngx_rtmp_create_abort(ngx_rtmp_session_t *s,
ngx_chain_t * ngx_rtmp_create_abort(ngx_rtmp_session_t *s,
uint32_t csid);
ngx_chain_t * ngx_rtmp_create_ack(ngx_rtmp_session_t *s,
ngx_chain_t * ngx_rtmp_create_ack(ngx_rtmp_session_t *s,
uint32_t seq);
ngx_chain_t * ngx_rtmp_create_ack_size(ngx_rtmp_session_t *s,
ngx_chain_t * ngx_rtmp_create_ack_size(ngx_rtmp_session_t *s,
uint32_t ack_size);
ngx_chain_t * ngx_rtmp_create_bandwidth(ngx_rtmp_session_t *s,
ngx_chain_t * ngx_rtmp_create_bandwidth(ngx_rtmp_session_t *s,
uint32_t ack_size, uint8_t limit_type);
ngx_int_t ngx_rtmp_send_chunk_size(ngx_rtmp_session_t *s,
ngx_int_t ngx_rtmp_send_chunk_size(ngx_rtmp_session_t *s,
uint32_t chunk_size);
ngx_int_t ngx_rtmp_send_abort(ngx_rtmp_session_t *s,
ngx_int_t ngx_rtmp_send_abort(ngx_rtmp_session_t *s,
uint32_t csid);
ngx_int_t ngx_rtmp_send_ack(ngx_rtmp_session_t *s,
ngx_int_t ngx_rtmp_send_ack(ngx_rtmp_session_t *s,
uint32_t seq);
ngx_int_t ngx_rtmp_send_ack_size(ngx_rtmp_session_t *s,
ngx_int_t ngx_rtmp_send_ack_size(ngx_rtmp_session_t *s,
uint32_t ack_size);
ngx_int_t ngx_rtmp_send_bandwidth(ngx_rtmp_session_t *s,
ngx_int_t ngx_rtmp_send_bandwidth(ngx_rtmp_session_t *s,
uint32_t ack_size, uint8_t limit_type);
/* User control messages */
ngx_chain_t * ngx_rtmp_create_stream_begin(ngx_rtmp_session_t *s,
ngx_chain_t * ngx_rtmp_create_stream_begin(ngx_rtmp_session_t *s,
uint32_t msid);
ngx_chain_t * ngx_rtmp_create_stream_eof(ngx_rtmp_session_t *s,
ngx_chain_t * ngx_rtmp_create_stream_eof(ngx_rtmp_session_t *s,
uint32_t msid);
ngx_chain_t * ngx_rtmp_create_stream_dry(ngx_rtmp_session_t *s,
ngx_chain_t * ngx_rtmp_create_stream_dry(ngx_rtmp_session_t *s,
uint32_t msid);
ngx_chain_t * ngx_rtmp_create_set_buflen(ngx_rtmp_session_t *s,
ngx_chain_t * ngx_rtmp_create_set_buflen(ngx_rtmp_session_t *s,
uint32_t msid, uint32_t buflen_msec);
ngx_chain_t * ngx_rtmp_create_recorded(ngx_rtmp_session_t *s,
ngx_chain_t * ngx_rtmp_create_recorded(ngx_rtmp_session_t *s,
uint32_t msid);
ngx_chain_t * ngx_rtmp_create_ping_request(ngx_rtmp_session_t *s,
uint32_t timestamp);
ngx_chain_t * ngx_rtmp_create_ping_response(ngx_rtmp_session_t *s,
ngx_chain_t * ngx_rtmp_create_ping_response(ngx_rtmp_session_t *s,
uint32_t timestamp);
ngx_int_t ngx_rtmp_send_stream_begin(ngx_rtmp_session_t *s,
ngx_int_t ngx_rtmp_send_stream_begin(ngx_rtmp_session_t *s,
uint32_t msid);
ngx_int_t ngx_rtmp_send_stream_eof(ngx_rtmp_session_t *s,
ngx_int_t ngx_rtmp_send_stream_eof(ngx_rtmp_session_t *s,
uint32_t msid);
ngx_int_t ngx_rtmp_send_stream_dry(ngx_rtmp_session_t *s,
ngx_int_t ngx_rtmp_send_stream_dry(ngx_rtmp_session_t *s,
uint32_t msid);
ngx_int_t ngx_rtmp_send_set_buflen(ngx_rtmp_session_t *s,
ngx_int_t ngx_rtmp_send_set_buflen(ngx_rtmp_session_t *s,
uint32_t msid, uint32_t buflen_msec);
ngx_int_t ngx_rtmp_send_recorded(ngx_rtmp_session_t *s,
ngx_int_t ngx_rtmp_send_recorded(ngx_rtmp_session_t *s,
uint32_t msid);
ngx_int_t ngx_rtmp_send_ping_request(ngx_rtmp_session_t *s,
uint32_t timestamp);
ngx_int_t ngx_rtmp_send_ping_response(ngx_rtmp_session_t *s,
ngx_int_t ngx_rtmp_send_ping_response(ngx_rtmp_session_t *s,
uint32_t timestamp);
/* AMF sender/receiver */
ngx_int_t ngx_rtmp_append_amf(ngx_rtmp_session_t *s,
ngx_chain_t **first, ngx_chain_t **last,
ngx_chain_t **first, ngx_chain_t **last,
ngx_rtmp_amf_elt_t *elts, size_t nelts);
ngx_int_t ngx_rtmp_receive_amf(ngx_rtmp_session_t *s, ngx_chain_t *in,
ngx_int_t ngx_rtmp_receive_amf(ngx_rtmp_session_t *s, ngx_chain_t *in,
ngx_rtmp_amf_elt_t *elts, size_t nelts);
ngx_chain_t * ngx_rtmp_create_amf(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
@ -569,6 +583,12 @@ ngx_int_t ngx_rtmp_send_status(ngx_rtmp_session_t *s, char *code,
ngx_int_t ngx_rtmp_send_play_status(ngx_rtmp_session_t *s, char *code,
char* level, ngx_uint_t duration, ngx_uint_t bytes);
ngx_int_t ngx_rtmp_send_sample_access(ngx_rtmp_session_t *s);
ngx_int_t ngx_rtmp_send_redirect_status(ngx_rtmp_session_t *s,
char *callMethod, char *desc, ngx_str_t to_url);
ngx_int_t ngx_rtmp_send_close_method(ngx_rtmp_session_t *s, char *methodName);
ngx_int_t ngx_rtmp_send_fcpublish(ngx_rtmp_session_t *s, u_char *desc);
ngx_int_t ngx_rtmp_send_fcunpublish(ngx_rtmp_session_t *s, u_char *desc);
ngx_int_t ngx_rtmp_send_fi(ngx_rtmp_session_t *s);
/* Frame types */
@ -596,7 +616,13 @@ extern ngx_rtmp_bandwidth_t ngx_rtmp_bw_in;
extern ngx_uint_t ngx_rtmp_naccepted;
#if (nginx_version >= 1007011)
extern ngx_queue_t ngx_rtmp_init_queue;
#elif (nginx_version >= 1007005)
extern ngx_thread_volatile ngx_queue_t ngx_rtmp_init_queue;
#else
extern ngx_thread_volatile ngx_event_t *ngx_rtmp_init_queue;
#endif
extern ngx_uint_t ngx_rtmp_max_module;
extern ngx_module_t ngx_rtmp_core_module;

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -17,11 +18,11 @@ static ngx_rtmp_play_pt next_play;
#define NGX_RTMP_ACCESS_PLAY 0x02
static char * ngx_rtmp_access_rule(ngx_conf_t *cf, ngx_command_t *cmd,
static char * ngx_rtmp_access_rule(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_int_t ngx_rtmp_access_postconfiguration(ngx_conf_t *cf);
static void * ngx_rtmp_access_create_app_conf(ngx_conf_t *cf);
static char * ngx_rtmp_access_merge_app_conf(ngx_conf_t *cf,
static char * ngx_rtmp_access_merge_app_conf(ngx_conf_t *cf,
void *parent, void *child);
@ -330,7 +331,7 @@ ngx_rtmp_access_rule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
#if (NGX_HAVE_INET6)
ngx_rtmp_access_rule6_t *rule6;
#endif
size_t n;
size_t n;
ngx_uint_t flags;
ngx_memzero(&cidr, sizeof(ngx_cidr_t));
@ -341,7 +342,7 @@ ngx_rtmp_access_rule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
flags = 0;
if (cf->args->nelts == 2) {
flags = NGX_RTMP_ACCESS_PUBLISH | NGX_RTMP_ACCESS_PLAY;
} else {
@ -355,7 +356,7 @@ ngx_rtmp_access_rule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
continue;
}
if (value[n].len == sizeof("play") - 1 &&
ngx_strcmp(value[1].data, "play") == 0)
{
@ -408,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 */
@ -429,7 +430,7 @@ ngx_rtmp_access_rule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
}
static ngx_int_t
static ngx_int_t
ngx_rtmp_access_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
{
if (s->auto_pushed) {
@ -445,13 +446,20 @@ next:
}
static ngx_int_t
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

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -26,7 +27,7 @@ ngx_rtmp_amf_reverse_copy(void *dst, void* src, size_t len)
return dst;
}
#define NGX_RTMP_AMF_DEBUG_SIZE 16
#define NGX_RTMP_AMF_DEBUG_SIZE 72
#ifdef NGX_DEBUG
static void
@ -88,7 +89,7 @@ ngx_rtmp_amf_get(ngx_rtmp_amf_ctx_t *ctx, void *p, size_t n)
}
ctx->offset = offset + n;
ctx->link = l;
#ifdef NGX_DEBUG
ngx_rtmp_amf_debug("read", ctx->log, (u_char*)op, on);
#endif
@ -168,8 +169,8 @@ ngx_rtmp_amf_put(ngx_rtmp_amf_ctx_t *ctx, void *p, size_t n)
}
static ngx_int_t
ngx_rtmp_amf_read_object(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
static ngx_int_t
ngx_rtmp_amf_read_object(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
size_t nelts)
{
uint8_t type;
@ -196,8 +197,15 @@ ngx_rtmp_amf_read_object(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
}
#endif
/* read key */
if (ngx_rtmp_amf_get(ctx, buf, 2) != NGX_OK)
switch (ngx_rtmp_amf_get(ctx, buf, 2)) {
case NGX_DONE:
/* Envivio sends unfinalized arrays */
return NGX_OK;
case NGX_OK:
break;
default:
return NGX_ERROR;
}
ngx_rtmp_amf_reverse_copy(&len, buf, 2);
@ -219,7 +227,7 @@ ngx_rtmp_amf_read_object(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
/* TODO: if we require array to be sorted on name
* then we could be able to use binary search */
for(n = 0; n < nelts
for(n = 0; n < nelts
&& (len != elts[n].name.len
|| ngx_strncmp(name, elts[n].name.data, len));
++n);
@ -238,8 +246,8 @@ ngx_rtmp_amf_read_object(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
}
static ngx_int_t
ngx_rtmp_amf_read_array(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
static ngx_int_t
ngx_rtmp_amf_read_array(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
size_t nelts)
{
uint32_t len;
@ -261,8 +269,8 @@ ngx_rtmp_amf_read_array(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
}
static ngx_int_t
ngx_rtmp_amf_read_variant(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
static ngx_int_t
ngx_rtmp_amf_read_variant(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
size_t nelts)
{
uint8_t type;
@ -298,8 +306,8 @@ ngx_rtmp_amf_is_compatible_type(uint8_t t1, uint8_t t2)
}
ngx_int_t
ngx_rtmp_amf_read(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
ngx_int_t
ngx_rtmp_amf_read(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
size_t nelts)
{
void *data;
@ -320,14 +328,15 @@ 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;
}
type = type8;
data = (elts &&
data = (elts &&
ngx_rtmp_amf_is_compatible_type(
(uint8_t) (elts->type & 0xff), (uint8_t) type))
? elts->data
@ -390,20 +399,21 @@ 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,
if (ngx_rtmp_amf_read_object(ctx, data,
data && elts ? elts->len / sizeof(ngx_rtmp_amf_elt_t) : 0
) != NGX_OK)
) != NGX_OK)
{
return NGX_ERROR;
}
break;
case NGX_RTMP_AMF_ARRAY:
if (ngx_rtmp_amf_read_array(ctx, data,
if (ngx_rtmp_amf_read_array(ctx, data,
data && elts ? elts->len / sizeof(ngx_rtmp_amf_elt_t) : 0
) != NGX_OK)
) != NGX_OK)
{
return NGX_ERROR;
}
@ -417,7 +427,7 @@ ngx_rtmp_amf_read(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
return NGX_ERROR;
}
break;
case NGX_RTMP_AMF_INT8:
if (ngx_rtmp_amf_get(ctx, data, 1) != NGX_OK) {
return NGX_ERROR;
@ -454,7 +464,7 @@ ngx_rtmp_amf_read(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
}
static ngx_int_t
static ngx_int_t
ngx_rtmp_amf_write_object(ngx_rtmp_amf_ctx_t *ctx,
ngx_rtmp_amf_elt_t *elts, size_t nelts)
{
@ -466,9 +476,9 @@ ngx_rtmp_amf_write_object(ngx_rtmp_amf_ctx_t *ctx,
len = (uint16_t) elts[n].name.len;
if (ngx_rtmp_amf_put(ctx,
ngx_rtmp_amf_reverse_copy(buf,
&len, 2), 2) != NGX_OK)
if (ngx_rtmp_amf_put(ctx,
ngx_rtmp_amf_reverse_copy(buf,
&len, 2), 2) != NGX_OK)
{
return NGX_ERROR;
}
@ -490,7 +500,7 @@ ngx_rtmp_amf_write_object(ngx_rtmp_amf_ctx_t *ctx,
}
static ngx_int_t
static ngx_int_t
ngx_rtmp_amf_write_array(ngx_rtmp_amf_ctx_t *ctx,
ngx_rtmp_amf_elt_t *elts, size_t nelts)
{
@ -499,9 +509,9 @@ ngx_rtmp_amf_write_array(ngx_rtmp_amf_ctx_t *ctx,
u_char buf[4];
len = nelts;
if (ngx_rtmp_amf_put(ctx,
ngx_rtmp_amf_reverse_copy(buf,
&len, 4), 4) != NGX_OK)
if (ngx_rtmp_amf_put(ctx,
ngx_rtmp_amf_reverse_copy(buf,
&len, 4), 4) != NGX_OK)
{
return NGX_ERROR;
}
@ -516,7 +526,7 @@ ngx_rtmp_amf_write_array(ngx_rtmp_amf_ctx_t *ctx,
}
ngx_int_t
ngx_int_t
ngx_rtmp_amf_write(ngx_rtmp_amf_ctx_t *ctx,
ngx_rtmp_amf_elt_t *elts, size_t nelts)
{
@ -544,9 +554,9 @@ ngx_rtmp_amf_write(ngx_rtmp_amf_ctx_t *ctx,
switch(type) {
case NGX_RTMP_AMF_NUMBER:
if (ngx_rtmp_amf_put(ctx,
ngx_rtmp_amf_reverse_copy(buf,
data, 8), 8) != NGX_OK)
if (ngx_rtmp_amf_put(ctx,
ngx_rtmp_amf_reverse_copy(buf,
data, 8), 8) != NGX_OK)
{
return NGX_ERROR;
}
@ -563,9 +573,9 @@ ngx_rtmp_amf_write(ngx_rtmp_amf_ctx_t *ctx,
len = (uint16_t) ngx_strlen((u_char*) data);
}
if (ngx_rtmp_amf_put(ctx,
ngx_rtmp_amf_reverse_copy(buf,
&len, 2), 2) != NGX_OK)
if (ngx_rtmp_amf_put(ctx,
ngx_rtmp_amf_reverse_copy(buf,
&len, 2), 2) != NGX_OK)
{
return NGX_ERROR;
}
@ -584,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;
@ -596,7 +607,7 @@ ngx_rtmp_amf_write(ngx_rtmp_amf_ctx_t *ctx,
break;
case NGX_RTMP_AMF_ARRAY:
if (ngx_rtmp_amf_write_array(ctx, data,
if (ngx_rtmp_amf_write_array(ctx, data,
elts[n].len / sizeof(ngx_rtmp_amf_elt_t)) != NGX_OK)
{
return NGX_ERROR;
@ -611,8 +622,8 @@ ngx_rtmp_amf_write(ngx_rtmp_amf_ctx_t *ctx,
case NGX_RTMP_AMF_INT16:
if (ngx_rtmp_amf_put(ctx,
ngx_rtmp_amf_reverse_copy(buf,
data, 2), 2) != NGX_OK)
ngx_rtmp_amf_reverse_copy(buf,
data, 2), 2) != NGX_OK)
{
return NGX_ERROR;
}
@ -620,8 +631,8 @@ ngx_rtmp_amf_write(ngx_rtmp_amf_ctx_t *ctx,
case NGX_RTMP_AMF_INT32:
if (ngx_rtmp_amf_put(ctx,
ngx_rtmp_amf_reverse_copy(buf,
data, 4), 4) != NGX_OK)
ngx_rtmp_amf_reverse_copy(buf,
data, 4), 4) != NGX_OK)
{
return NGX_ERROR;
}

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -18,9 +19,9 @@ static void ngx_rtmp_auto_push_exit_process(ngx_cycle_t *cycle);
static void * ngx_rtmp_auto_push_create_conf(ngx_cycle_t *cf);
static char * ngx_rtmp_auto_push_init_conf(ngx_cycle_t *cycle, void *conf);
#if (NGX_HAVE_UNIX_DOMAIN)
static ngx_int_t ngx_rtmp_auto_push_publish(ngx_rtmp_session_t *s,
static ngx_int_t ngx_rtmp_auto_push_publish(ngx_rtmp_session_t *s,
ngx_rtmp_publish_t *v);
static ngx_int_t ngx_rtmp_auto_push_delete_stream(ngx_rtmp_session_t *s,
static ngx_int_t ngx_rtmp_auto_push_delete_stream(ngx_rtmp_session_t *s,
ngx_rtmp_delete_stream_t *v);
#endif
@ -92,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"
@ -101,7 +130,7 @@ ngx_rtmp_auto_push_init_process(ngx_cycle_t *cycle)
#if (NGX_HAVE_UNIX_DOMAIN)
ngx_rtmp_auto_push_conf_t *apcf;
ngx_listening_t *ls, *lss;
struct sockaddr_un *sun;
struct sockaddr_un *saun;
int reuseaddr;
ngx_socket_t s;
size_t n;
@ -111,7 +140,7 @@ ngx_rtmp_auto_push_init_process(ngx_cycle_t *cycle)
return NGX_OK;
}
apcf = (ngx_rtmp_auto_push_conf_t *) ngx_get_conf(cycle->conf_ctx,
apcf = (ngx_rtmp_auto_push_conf_t *) ngx_get_conf(cycle->conf_ctx,
ngx_rtmp_auto_push_module);
if (apcf->auto_push == 0) {
return NGX_OK;
@ -153,29 +182,29 @@ ngx_rtmp_auto_push_init_process(ngx_cycle_t *cycle)
/* Disable unix socket client address extraction
* from accept call
* Nginx generates bad addr_text with this enabled */
ls->addr_ntop = 0;
ls->addr_ntop = 0;
ls->socklen = sizeof(struct sockaddr_un);
sun = ngx_pcalloc(cycle->pool, ls->socklen);
ls->sockaddr = (struct sockaddr *) sun;
saun = ngx_pcalloc(cycle->pool, ls->socklen);
ls->sockaddr = (struct sockaddr *) saun;
if (ls->sockaddr == NULL) {
return NGX_ERROR;
}
sun->sun_family = AF_UNIX;
*ngx_snprintf((u_char *) sun->sun_path, sizeof(sun->sun_path),
"%V/" NGX_RTMP_AUTO_PUSH_SOCKNAME ".%i",
saun->sun_family = AF_UNIX;
*ngx_snprintf((u_char *) saun->sun_path, sizeof(saun->sun_path),
"%V/" NGX_RTMP_AUTO_PUSH_SOCKNAME ".%i",
&apcf->socket_dir, ngx_process_slot)
= 0;
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, cycle->log, 0,
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, cycle->log, 0,
"auto_push: create socket '%s'",
sun->sun_path);
saun->sun_path);
if (ngx_file_info(sun->sun_path, &fi) != ENOENT) {
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, cycle->log, 0,
if (ngx_file_info(saun->sun_path, &fi) != ENOENT) {
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, cycle->log, 0,
"auto_push: delete existing socket '%s'",
sun->sun_path);
ngx_delete_file(sun->sun_path);
saun->sun_path);
ngx_delete_file(saun->sun_path);
}
ngx_str_set(&ls->addr_text, "worker_socket");
@ -204,7 +233,7 @@ ngx_rtmp_auto_push_init_process(ngx_cycle_t *cycle)
}
}
if (bind(s, (struct sockaddr *) sun, sizeof(*sun)) == -1) {
if (bind(s, (struct sockaddr *) saun, sizeof(*saun)) == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,
ngx_nonblocking_n " worker_socket bind failed");
goto sock_error;
@ -227,7 +256,7 @@ sock_error:
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,
ngx_close_socket_n " worker_socket failed");
}
ngx_delete_file(sun->sun_path);
ngx_delete_file(saun->sun_path);
return NGX_ERROR;
@ -246,14 +275,14 @@ ngx_rtmp_auto_push_exit_process(ngx_cycle_t *cycle)
ngx_rtmp_auto_push_conf_t *apcf;
u_char path[NGX_MAX_PATH];
apcf = (ngx_rtmp_auto_push_conf_t *) ngx_get_conf(cycle->conf_ctx,
apcf = (ngx_rtmp_auto_push_conf_t *) ngx_get_conf(cycle->conf_ctx,
ngx_rtmp_auto_push_module);
if (apcf->auto_push == 0) {
return;
}
*ngx_snprintf(path, sizeof(path),
"%V/" NGX_RTMP_AUTO_PUSH_SOCKNAME ".%i",
&apcf->socket_dir, ngx_process_slot)
"%V/" NGX_RTMP_AUTO_PUSH_SOCKNAME ".%i",
&apcf->socket_dir, ngx_process_slot)
= 0;
ngx_delete_file(path);
@ -318,12 +347,12 @@ ngx_rtmp_auto_push_reconnect(ngx_event_t *ev)
ngx_core_conf_t *ccf;
ngx_file_info_t fi;
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"auto_push: reconnect");
apcf = (ngx_rtmp_auto_push_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
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;
}
@ -365,7 +394,7 @@ ngx_rtmp_auto_push_reconnect(ngx_event_t *ev)
ngx_memzero(&at.url, sizeof(at.url));
u = &at.url.url;
p = ngx_snprintf(path, sizeof(path) - 1,
"unix:%V/" NGX_RTMP_AUTO_PUSH_SOCKNAME ".%i",
"unix:%V/" NGX_RTMP_AUTO_PUSH_SOCKNAME ".%i",
&apcf->socket_dir, n);
*p = 0;
@ -387,12 +416,12 @@ ngx_rtmp_auto_push_reconnect(ngx_event_t *ev)
continue;
}
p = ngx_snprintf(flash_ver, sizeof(flash_ver) - 1, "APSH %i,%i",
p = ngx_snprintf(flash_ver, sizeof(flash_ver) - 1, "APSH %i,%i",
(ngx_int_t) ngx_process_slot, (ngx_int_t) ngx_pid);
at.flash_ver.data = flash_ver;
at.flash_ver.len = p - flash_ver;
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"auto_push: connect slot=%i pid=%P socket='%s' name='%s'",
n, pid, path, ctx->name);
@ -454,20 +483,20 @@ ngx_rtmp_auto_push_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
goto next;
}
apcf = (ngx_rtmp_auto_push_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
apcf = (ngx_rtmp_auto_push_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_rtmp_auto_push_module);
if (apcf->auto_push == 0) {
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,
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));
@ -476,7 +505,7 @@ ngx_rtmp_auto_push_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
ctx->push_evt.log = s->connection->log;
ctx->push_evt.handler = ngx_rtmp_auto_push_reconnect;
ctx->slots = ngx_pcalloc(s->connection->pool,
ctx->slots = ngx_pcalloc(s->connection->pool,
sizeof(ngx_int_t) * NGX_MAX_PROCESSES);
if (ctx->slots == NULL) {
goto next;
@ -493,7 +522,7 @@ next:
static ngx_int_t
ngx_rtmp_auto_push_delete_stream(ngx_rtmp_session_t *s,
ngx_rtmp_auto_push_delete_stream(ngx_rtmp_session_t *s,
ngx_rtmp_delete_stream_t *v)
{
ngx_rtmp_auto_push_conf_t *apcf;
@ -501,13 +530,13 @@ ngx_rtmp_auto_push_delete_stream(ngx_rtmp_session_t *s,
ngx_rtmp_relay_ctx_t *rctx;
ngx_int_t slot;
apcf = (ngx_rtmp_auto_push_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
apcf = (ngx_rtmp_auto_push_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
ngx_rtmp_auto_push_module);
if (apcf->auto_push == 0) {
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);
@ -517,21 +546,21 @@ ngx_rtmp_auto_push_delete_stream(ngx_rtmp_session_t *s,
/* skip non-relays & publishers */
rctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module);
if (rctx == NULL ||
if (rctx == NULL ||
rctx->tag != &ngx_rtmp_auto_push_module ||
rctx->publish == NULL)
rctx->publish == NULL)
{
goto next;
}
slot = (ngx_process_t *) rctx->data - &ngx_processes[0];
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"auto_push: disconnect slot=%i app='%V' name='%V'",
slot, &rctx->app, &rctx->name);
pctx = ngx_rtmp_get_module_ctx(rctx->publish->session,
ngx_rtmp_auto_push_module);
pctx = ngx_rtmp_get_module_ctx(rctx->publish->session,
ngx_rtmp_auto_push_index_module);
if (pctx == NULL) {
goto next;
}

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -8,11 +9,11 @@
#include "ngx_rtmp_bandwidth.h"
void
void
ngx_rtmp_update_bandwidth(ngx_rtmp_bandwidth_t *bw, uint32_t bytes)
{
if (ngx_cached_time->sec > bw->intl_end) {
bw->bandwidth = ngx_cached_time->sec >
bw->bandwidth = ngx_cached_time->sec >
bw->intl_end + NGX_RTMP_BANDWIDTH_INTERVAL
? 0
: bw->intl_bytes / NGX_RTMP_BANDWIDTH_INTERVAL;

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -12,7 +13,7 @@
/* Bandwidth update interval in seconds */
#define NGX_RTMP_BANDWIDTH_INTERVAL 60
#define NGX_RTMP_BANDWIDTH_INTERVAL 10
typedef struct {
@ -28,4 +29,3 @@ void ngx_rtmp_update_bandwidth(ngx_rtmp_bandwidth_t *bw, uint32_t bytes);
#endif /* _NGX_RTMP_BANDWIDTH_H_INCLUDED_ */

63
ngx_rtmp_bitop.c Normal file
View file

@ -0,0 +1,63 @@
/*
* Copyright (C) Roman Arutyunyan
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include "ngx_rtmp_bitop.h"
void
ngx_rtmp_bit_init_reader(ngx_rtmp_bit_reader_t *br, u_char *pos, u_char *last)
{
ngx_memzero(br, sizeof(ngx_rtmp_bit_reader_t));
br->pos = pos;
br->last = last;
}
uint64_t
ngx_rtmp_bit_read(ngx_rtmp_bit_reader_t *br, ngx_uint_t n)
{
uint64_t v;
ngx_uint_t d;
v = 0;
while (n) {
if (br->pos >= br->last) {
br->err = 1;
return 0;
}
d = (br->offs + n > 8 ? (ngx_uint_t) (8 - br->offs) : n);
v <<= d;
v += (*br->pos >> (8 - br->offs - d)) & ((u_char) 0xff >> (8 - d));
br->offs += d;
n -= d;
if (br->offs == 8) {
br->pos++;
br->offs = 0;
}
}
return v;
}
uint64_t
ngx_rtmp_bit_read_golomb(ngx_rtmp_bit_reader_t *br)
{
ngx_uint_t n;
for (n = 0; ngx_rtmp_bit_read(br, 1) == 0 && !br->err; n++);
return ((uint64_t) 1 << n) + ngx_rtmp_bit_read(br, n) - 1;
}

46
ngx_rtmp_bitop.h Normal file
View file

@ -0,0 +1,46 @@
/*
* Copyright (C) Roman Arutyunyan
*/
#ifndef _NGX_RTMP_BITOP_H_INCLUDED_
#define _NGX_RTMP_BITOP_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
typedef struct {
u_char *pos;
u_char *last;
ngx_uint_t offs;
ngx_uint_t err;
} ngx_rtmp_bit_reader_t;
void ngx_rtmp_bit_init_reader(ngx_rtmp_bit_reader_t *br, u_char *pos,
u_char *last);
uint64_t ngx_rtmp_bit_read(ngx_rtmp_bit_reader_t *br, ngx_uint_t n);
uint64_t ngx_rtmp_bit_read_golomb(ngx_rtmp_bit_reader_t *br);
#define ngx_rtmp_bit_read_err(br) ((br)->err)
#define ngx_rtmp_bit_read_eof(br) ((br)->pos == (br)->last)
#define ngx_rtmp_bit_read_8(br) \
((uint8_t) ngx_rtmp_bit_read(br, 8))
#define ngx_rtmp_bit_read_16(br) \
((uint16_t) ngx_rtmp_bit_read(br, 16))
#define ngx_rtmp_bit_read_32(br) \
((uint32_t) ngx_rtmp_bit_read(br, 32))
#define ngx_rtmp_bit_read_64(br) \
((uint64_t) ngx_rtmp_read(br, 64))
#endif /* _NGX_RTMP_BITOP_H_INCLUDED_ */

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -43,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;
@ -61,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);
@ -93,8 +96,8 @@ ngx_module_t ngx_rtmp_cmd_module = {
};
static void
ngx_rtmp_cmd_fill_args(u_char name[NGX_RTMP_MAX_NAME],
void
ngx_rtmp_cmd_fill_args(u_char name[NGX_RTMP_MAX_NAME],
u_char args[NGX_RTMP_MAX_ARGS])
{
u_char *p;
@ -119,11 +122,11 @@ ngx_rtmp_cmd_connect_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
static ngx_rtmp_amf_elt_t in_cmd[] = {
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_string("app"),
v.app, sizeof(v.app) },
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_string("flashVer"),
v.flashver, sizeof(v.flashver) },
@ -164,14 +167,16 @@ ngx_rtmp_cmd_connect_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
};
ngx_memzero(&v, sizeof(v));
if (ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
if (ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
{
return NGX_ERROR;
}
len = ngx_strlen(v.app);
if (len && v.app[len - 1] == '/') {
if (len > 10 && !ngx_memcmp(v.app + len - 10, "/_definst_", 10)) {
v.app[len - 10] = 0;
} else if (len && v.app[len - 1] == '/') {
v.app[len - 1] = 0;
}
@ -180,7 +185,7 @@ ngx_rtmp_cmd_connect_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"connect: app='%s' args='%s' flashver='%s' swf_url='%s' "
"tc_url='%s' page_url='%s' acodecs=%uD vcodecs=%uD "
"object_encoding=%ui",
"object_encoding=%ui",
v.app, v.args, v.flashver, v.swf_url, v.tc_url, v.page_url,
(uint32_t)v.acodecs, (uint32_t)v.vcodecs,
(ngx_int_t)v.object_encoding);
@ -204,10 +209,10 @@ ngx_rtmp_cmd_connect(ngx_rtmp_session_t *s, ngx_rtmp_connect_t *v)
static ngx_rtmp_amf_elt_t out_obj[] = {
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_string("fmsVer"),
NGX_RTMP_FMS_VERSION, 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("capabilities"),
&capabilities, 0 },
@ -215,13 +220,13 @@ ngx_rtmp_cmd_connect(ngx_rtmp_session_t *s, ngx_rtmp_connect_t *v)
static ngx_rtmp_amf_elt_t out_inf[] = {
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_string("level"),
"status", 0 },
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_string("code"),
"NetConnection.Connect.Success", 0 },
"NetConnection.Connect.Success", 0 },
{ NGX_RTMP_AMF_STRING,
ngx_string("description"),
@ -235,7 +240,7 @@ ngx_rtmp_cmd_connect(ngx_rtmp_session_t *s, ngx_rtmp_connect_t *v)
static ngx_rtmp_amf_elt_t out_elts[] = {
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
ngx_null_string,
"_result", 0 },
{ NGX_RTMP_AMF_NUMBER,
@ -252,7 +257,7 @@ ngx_rtmp_cmd_connect(ngx_rtmp_session_t *s, ngx_rtmp_connect_t *v)
};
if (s->connected) {
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"connect: duplicate connection");
return NGX_ERROR;
}
@ -304,7 +309,7 @@ ngx_rtmp_cmd_connect(ngx_rtmp_session_t *s, ngx_rtmp_connect_t *v)
}
if (s->app_conf == NULL) {
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"connect: application not found: '%V'", &s->app);
return NGX_ERROR;
}
@ -312,12 +317,12 @@ ngx_rtmp_cmd_connect(ngx_rtmp_session_t *s, ngx_rtmp_connect_t *v)
object_encoding = v->object_encoding;
return ngx_rtmp_send_ack_size(s, cscf->ack_window) != NGX_OK ||
ngx_rtmp_send_bandwidth(s, cscf->ack_window,
ngx_rtmp_send_bandwidth(s, cscf->ack_window,
NGX_RTMP_LIMIT_DYNAMIC) != NGX_OK ||
ngx_rtmp_send_chunk_size(s, cscf->chunk_size) != NGX_OK ||
ngx_rtmp_send_amf(s, &h, out_elts,
sizeof(out_elts) / sizeof(out_elts[0]))
!= NGX_OK ? NGX_ERROR : NGX_OK;
!= NGX_OK ? NGX_ERROR : NGX_OK;
}
@ -329,13 +334,13 @@ ngx_rtmp_cmd_create_stream_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
static ngx_rtmp_amf_elt_t in_elts[] = {
{ NGX_RTMP_AMF_NUMBER,
ngx_null_string,
{ NGX_RTMP_AMF_NUMBER,
ngx_null_string,
&v.trans, sizeof(v.trans) },
};
if (ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
if (ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
{
return NGX_ERROR;
}
@ -350,7 +355,7 @@ static ngx_int_t
ngx_rtmp_cmd_create_stream(ngx_rtmp_session_t *s, ngx_rtmp_create_stream_t *v)
{
/* support one message stream per connection */
static double stream;
static double stream;
static double trans;
ngx_rtmp_header_t h;
@ -364,7 +369,7 @@ ngx_rtmp_cmd_create_stream(ngx_rtmp_session_t *s, ngx_rtmp_create_stream_t *v)
ngx_null_string,
&trans, 0 },
{ NGX_RTMP_AMF_NULL,
{ NGX_RTMP_AMF_NULL,
ngx_null_string,
NULL, 0 },
@ -400,7 +405,7 @@ ngx_rtmp_cmd_close_stream_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
&v.stream, 0 },
};
if (ngx_rtmp_receive_amf(s, in, in_elts,
if (ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
{
return NGX_ERROR;
@ -427,7 +432,7 @@ ngx_rtmp_cmd_delete_stream_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
static ngx_rtmp_amf_elt_t in_elts[] = {
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_null_string,
NULL, 0 },
@ -440,8 +445,8 @@ ngx_rtmp_cmd_delete_stream_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
&v.stream, 0 },
};
if (ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
if (ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
{
return NGX_ERROR;
}
@ -473,7 +478,7 @@ ngx_rtmp_cmd_publish_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
/* transaction is always 0 */
{ NGX_RTMP_AMF_NUMBER,
ngx_null_string,
ngx_null_string,
NULL, 0 },
{ NGX_RTMP_AMF_NULL,
@ -491,8 +496,8 @@ ngx_rtmp_cmd_publish_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_memzero(&v, sizeof(v));
if (ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
if (ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
{
return NGX_ERROR;
}
@ -522,7 +527,7 @@ ngx_rtmp_cmd_play_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
static ngx_rtmp_amf_elt_t in_elts[] = {
/* transaction is always 0 */
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_null_string,
NULL, 0 },
@ -549,7 +554,7 @@ ngx_rtmp_cmd_play_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_memzero(&v, sizeof(v));
if (ngx_rtmp_receive_amf(s, in, in_elts,
if (ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
{
return NGX_ERROR;
@ -560,8 +565,8 @@ ngx_rtmp_cmd_play_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"play: name='%s' args='%s' start=%i duration=%i "
"reset=%i silent=%i",
v.name, v.args, (ngx_int_t) v.start,
(ngx_int_t) v.duration, (ngx_int_t) v.reset,
v.name, v.args, (ngx_int_t) v.start,
(ngx_int_t) v.duration, (ngx_int_t) v.reset,
(ngx_int_t) v.silent);
return ngx_rtmp_play(s, &v);
@ -571,10 +576,75 @@ 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;
}
static ngx_int_t
ngx_rtmp_cmd_play2_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
static ngx_rtmp_play_t v;
static ngx_rtmp_close_stream_t vc;
static ngx_rtmp_amf_elt_t in_obj[] = {
{ NGX_RTMP_AMF_NUMBER,
ngx_string("start"),
&v.start, 0 },
{ NGX_RTMP_AMF_STRING,
ngx_string("streamName"),
&v.name, sizeof(v.name) },
};
static ngx_rtmp_amf_elt_t in_elts[] = {
/* transaction is always 0 */
{ NGX_RTMP_AMF_NUMBER,
ngx_null_string,
NULL, 0 },
{ NGX_RTMP_AMF_NULL,
ngx_null_string,
NULL, 0 },
{ NGX_RTMP_AMF_OBJECT,
ngx_null_string,
&in_obj, sizeof(in_obj) }
};
ngx_memzero(&v, sizeof(v));
if (ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
{
return NGX_ERROR;
}
ngx_rtmp_cmd_fill_args(v.name, v.args);
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"play2: name='%s' args='%s' start=%i",
v.name, v.args, (ngx_int_t) v.start);
/* continue from current timestamp */
if (v.start < 0) {
v.start = s->current_time;
}
ngx_memzero(&vc, sizeof(vc));
/* close_stream should be synchronous */
ngx_rtmp_close_stream(s, &vc);
return ngx_rtmp_play(s, &v);
}
static ngx_int_t
ngx_rtmp_cmd_pause_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
@ -583,7 +653,7 @@ ngx_rtmp_cmd_pause_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
static ngx_rtmp_amf_elt_t in_elts[] = {
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_null_string,
NULL, 0 },
@ -602,8 +672,8 @@ ngx_rtmp_cmd_pause_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_memzero(&v, sizeof(v));
if (ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
if (ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
{
return NGX_ERROR;
}
@ -649,7 +719,7 @@ ngx_rtmp_cmd_seek_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
static ngx_rtmp_amf_elt_t in_elts[] = {
/* transaction is always 0 */
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_null_string,
NULL, 0 },
@ -664,8 +734,8 @@ ngx_rtmp_cmd_seek_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_memzero(&v, sizeof(v));
if (ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
if (ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
{
return NGX_ERROR;
}
@ -720,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 },
@ -727,6 +805,7 @@ static ngx_rtmp_amf_handler_t ngx_rtmp_cmd_map[] = {
{ ngx_string("deleteStream"), ngx_rtmp_cmd_delete_stream_init },
{ ngx_string("publish"), ngx_rtmp_cmd_publish_init },
{ ngx_string("play"), ngx_rtmp_cmd_play_init },
{ ngx_string("play2"), ngx_rtmp_cmd_play2_init },
{ ngx_string("seek"), ngx_rtmp_cmd_seek_init },
{ ngx_string("pause"), ngx_rtmp_cmd_pause_init },
{ ngx_string("pauseraw"), ngx_rtmp_cmd_pause_init },
@ -743,7 +822,7 @@ ngx_rtmp_cmd_postconfiguration(ngx_conf_t *cf)
cmcf = ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_core_module);
/* redirect disconnects to deleteStream
/* redirect disconnects to deleteStream
* to free client modules from registering
* disconnect callback */
@ -785,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

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -13,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
@ -24,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;
@ -58,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];
@ -96,7 +103,11 @@ typedef struct {
} ngx_rtmp_set_buflen_t;
typedef ngx_int_t (*ngx_rtmp_connect_pt)(ngx_rtmp_session_t *s,
void ngx_rtmp_cmd_fill_args(u_char name[NGX_RTMP_MAX_NAME],
u_char args[NGX_RTMP_MAX_ARGS]);
typedef ngx_int_t (*ngx_rtmp_connect_pt)(ngx_rtmp_session_t *s,
ngx_rtmp_connect_t *v);
typedef ngx_int_t (*ngx_rtmp_disconnect_pt)(ngx_rtmp_session_t *s);
typedef ngx_int_t (*ngx_rtmp_create_stream_pt)(ngx_rtmp_session_t *s,
@ -105,13 +116,13 @@ typedef ngx_int_t (*ngx_rtmp_close_stream_pt)(ngx_rtmp_session_t *s,
ngx_rtmp_close_stream_t *v);
typedef ngx_int_t (*ngx_rtmp_delete_stream_pt)(ngx_rtmp_session_t *s,
ngx_rtmp_delete_stream_t *v);
typedef ngx_int_t (*ngx_rtmp_publish_pt)(ngx_rtmp_session_t *s,
typedef ngx_int_t (*ngx_rtmp_publish_pt)(ngx_rtmp_session_t *s,
ngx_rtmp_publish_t *v);
typedef ngx_int_t (*ngx_rtmp_play_pt)(ngx_rtmp_session_t *s,
typedef ngx_int_t (*ngx_rtmp_play_pt)(ngx_rtmp_session_t *s,
ngx_rtmp_play_t *v);
typedef ngx_int_t (*ngx_rtmp_seek_pt)(ngx_rtmp_session_t *s,
typedef ngx_int_t (*ngx_rtmp_seek_pt)(ngx_rtmp_session_t *s,
ngx_rtmp_seek_t *v);
typedef ngx_int_t (*ngx_rtmp_pause_pt)(ngx_rtmp_session_t *s,
typedef ngx_int_t (*ngx_rtmp_pause_pt)(ngx_rtmp_session_t *s,
ngx_rtmp_pause_t *v);
typedef ngx_int_t (*ngx_rtmp_stream_begin_pt)(ngx_rtmp_session_t *s,
@ -125,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;
@ -142,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

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -8,9 +9,57 @@
#include "ngx_rtmp_codec_module.h"
#include "ngx_rtmp_live_module.h"
#include "ngx_rtmp_cmd_module.h"
#include "ngx_rtmp_bitop.h"
#define NGX_RTMP_CODEC_META_OFF 0
#define NGX_RTMP_CODEC_META_ON 1
#define NGX_RTMP_CODEC_META_COPY 2
static void * ngx_rtmp_codec_create_app_conf(ngx_conf_t *cf);
static char * ngx_rtmp_codec_merge_app_conf(ngx_conf_t *cf,
void *parent, void *child);
static ngx_int_t ngx_rtmp_codec_postconfiguration(ngx_conf_t *cf);
static ngx_int_t ngx_rtmp_codec_reconstruct_meta(ngx_rtmp_session_t *s);
static ngx_int_t ngx_rtmp_codec_copy_meta(ngx_rtmp_session_t *s,
ngx_rtmp_header_t *h, ngx_chain_t *in);
static ngx_int_t ngx_rtmp_codec_prepare_meta(ngx_rtmp_session_t *s,
uint32_t timestamp);
static void ngx_rtmp_codec_parse_aac_header(ngx_rtmp_session_t *s,
ngx_chain_t *in);
static void ngx_rtmp_codec_parse_avc_header(ngx_rtmp_session_t *s,
ngx_chain_t *in);
#if (NGX_DEBUG)
static void ngx_rtmp_codec_dump_header(ngx_rtmp_session_t *s, const char *type,
ngx_chain_t *in);
#endif
typedef struct {
ngx_uint_t meta;
} ngx_rtmp_codec_app_conf_t;
static ngx_conf_enum_t ngx_rtmp_codec_meta_slots[] = {
{ ngx_string("off"), NGX_RTMP_CODEC_META_OFF },
{ ngx_string("on"), NGX_RTMP_CODEC_META_ON },
{ ngx_string("copy"), NGX_RTMP_CODEC_META_COPY },
{ ngx_null_string, 0 }
};
static ngx_command_t ngx_rtmp_codec_commands[] = {
{ ngx_string("meta"),
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_codec_app_conf_t, meta),
&ngx_rtmp_codec_meta_slots },
ngx_null_command
};
static ngx_rtmp_module_t ngx_rtmp_codec_module_ctx = {
@ -20,15 +69,15 @@ static ngx_rtmp_module_t ngx_rtmp_codec_module_ctx = {
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create app configuration */
NULL /* merge app configuration */
ngx_rtmp_codec_create_app_conf, /* create app configuration */
ngx_rtmp_codec_merge_app_conf /* merge app configuration */
};
ngx_module_t ngx_rtmp_codec_module = {
NGX_MODULE_V1,
&ngx_rtmp_codec_module_ctx, /* module context */
NULL, /* module directives */
ngx_rtmp_codec_commands, /* module directives */
NGX_RTMP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
@ -41,7 +90,7 @@ ngx_module_t ngx_rtmp_codec_module = {
};
static const char *
static const char *
audio_codecs[] = {
"",
"ADPCM",
@ -63,7 +112,7 @@ audio_codecs[] = {
};
static const char *
static const char *
video_codecs[] = {
"",
"Jpeg",
@ -76,7 +125,7 @@ video_codecs[] = {
};
u_char *
u_char *
ngx_rtmp_get_audio_codec_name(ngx_uint_t id)
{
return (u_char *)(id < sizeof(audio_codecs) / sizeof(audio_codecs[0])
@ -85,7 +134,7 @@ ngx_rtmp_get_audio_codec_name(ngx_uint_t id)
}
u_char *
u_char *
ngx_rtmp_get_video_codec_name(ngx_uint_t id)
{
return (u_char *)(id < sizeof(video_codecs) / sizeof(video_codecs[0])
@ -109,7 +158,7 @@ ngx_rtmp_codec_get_next_version()
static ngx_int_t
ngx_rtmp_codec_disconnect(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_rtmp_codec_disconnect(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
ngx_rtmp_codec_ctx_t *ctx;
@ -142,28 +191,20 @@ ngx_rtmp_codec_disconnect(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
static ngx_int_t
ngx_rtmp_codec_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_rtmp_codec_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
ngx_rtmp_core_srv_conf_t *cscf;
ngx_rtmp_codec_ctx_t *ctx;
ngx_chain_t **header;
uint8_t fmt;
ngx_uint_t idx;
u_char *p;
static ngx_uint_t sample_rates[] =
static ngx_uint_t sample_rates[] =
{ 5512, 11025, 22050, 44100 };
static ngx_uint_t aac_sample_rates[] =
{ 96000, 88200, 64000, 48000,
44100, 32000, 24000, 22050,
16000, 12000, 11025, 8000,
7350, 0, 0, 0 };
if (h->type != NGX_RTMP_MSG_AUDIO && h->type != NGX_RTMP_MSG_VIDEO) {
return NGX_OK;
}
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
if (ctx == NULL) {
ctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_rtmp_codec_ctx_t));
@ -181,7 +222,7 @@ ngx_rtmp_codec_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ctx->audio_channels = (fmt & 0x01) + 1;
ctx->sample_size = (fmt & 0x02) ? 2 : 1;
if (ctx->aac_sample_rate == 0) {
if (ctx->sample_rate == 0) {
ctx->sample_rate = sample_rates[(fmt & 0x0c) >> 2];
}
} else {
@ -200,64 +241,16 @@ ngx_rtmp_codec_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
header = NULL;
if (h->type == NGX_RTMP_MSG_AUDIO) {
if (ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC) {
header = &ctx->aac_header;
if (in->buf->last - in->buf->pos > 3) {
p = in->buf->pos + 2;
/* MPEG-4 Audio Specific Config
5 bits: object type
if (object type == 31)
6 bits + 32: object type
--->4 bits: frequency index
if (frequency index == 15)
24 bits: frequency
4 bits: channel configuration
var bits: AOT Specific Config
*/
if ((p[0] >> 3) == 0x1f) {
idx = (p[1] >> 1) & 0x0f;
} else {
idx = ((p[0] << 1) & 0x0f) | (p[1] >> 7);
}
#ifdef NGX_DEBUG
{
u_char buf[256], *p, *pp;
u_char hex[] = "01234567890abcdef";
for (pp = buf, p = in->buf->pos;
p < in->buf->last && pp < buf + sizeof(buf) - 1;
++p)
{
*pp++ = hex[*p >> 4];
*pp++ = hex[*p & 0x0f];
}
*pp = 0;
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"codec: AAC header: %s", buf);
}
#endif
ctx->aac_sample_rate = aac_sample_rates[idx];
ctx->sample_rate = ctx->aac_sample_rate;
}
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"codec: AAC header arrived, sample_rate=%ui",
ctx->aac_sample_rate);
ngx_rtmp_codec_parse_aac_header(s, in);
}
} else {
if (ctx->video_codec_id == NGX_RTMP_VIDEO_H264) {
header = &ctx->avc_header;
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"codec: AVC/H264 header arrived");
ngx_rtmp_codec_parse_avc_header(s, in);
}
}
@ -275,13 +268,326 @@ ngx_rtmp_codec_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
}
static void
ngx_rtmp_codec_parse_aac_header(ngx_rtmp_session_t *s, ngx_chain_t *in)
{
ngx_uint_t idx;
ngx_rtmp_codec_ctx_t *ctx;
ngx_rtmp_bit_reader_t br;
static ngx_uint_t aac_sample_rates[] =
{ 96000, 88200, 64000, 48000,
44100, 32000, 24000, 22050,
16000, 12000, 11025, 8000,
7350, 0, 0, 0 };
#if (NGX_DEBUG)
ngx_rtmp_codec_dump_header(s, "aac", in);
#endif
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
ngx_rtmp_bit_init_reader(&br, in->buf->pos, in->buf->last);
ngx_rtmp_bit_read(&br, 16);
ctx->aac_profile = (ngx_uint_t) ngx_rtmp_bit_read(&br, 5);
if (ctx->aac_profile == 31) {
ctx->aac_profile = (ngx_uint_t) ngx_rtmp_bit_read(&br, 6) + 32;
}
idx = (ngx_uint_t) ngx_rtmp_bit_read(&br, 4);
if (idx == 15) {
ctx->sample_rate = (ngx_uint_t) ngx_rtmp_bit_read(&br, 24);
} else {
ctx->sample_rate = aac_sample_rates[idx];
}
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;
}
ctx->aac_sbr = 1;
idx = (ngx_uint_t) ngx_rtmp_bit_read(&br, 4);
if (idx == 15) {
ctx->sample_rate = (ngx_uint_t) ngx_rtmp_bit_read(&br, 24);
} else {
ctx->sample_rate = aac_sample_rates[idx];
}
ctx->aac_profile = (ngx_uint_t) ngx_rtmp_bit_read(&br, 5);
if (ctx->aac_profile == 31) {
ctx->aac_profile = (ngx_uint_t) ngx_rtmp_bit_read(&br, 6) + 32;
}
}
/* MPEG-4 Audio Specific Config
5 bits: object type
if (object type == 31)
6 bits + 32: object type
4 bits: frequency index
if (frequency index == 15)
24 bits: frequency
4 bits: channel configuration
if (object_type == 5)
4 bits: frequency index
if (frequency index == 15)
24 bits: frequency
5 bits: object type
if (object type == 31)
6 bits + 32: object type
var bits: AOT Specific Config
*/
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"codec: aac header profile=%ui, "
"sample_rate=%ui, chan_conf=%ui",
ctx->aac_profile, ctx->sample_rate, ctx->aac_chan_conf);
}
static void
ngx_rtmp_codec_parse_avc_header(ngx_rtmp_session_t *s, ngx_chain_t *in)
{
ngx_uint_t profile_idc, width, height, crop_left, crop_right,
crop_top, crop_bottom, frame_mbs_only, n, cf_n, cf_idc,
// num_ref_frames;
num_ref_frames, sl_size, sl_index, sl_udelta;
ngx_int_t sl_last, sl_next, sl_delta;
ngx_rtmp_codec_ctx_t *ctx;
ngx_rtmp_bit_reader_t br;
#if (NGX_DEBUG)
ngx_rtmp_codec_dump_header(s, "avc", in);
#endif
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
ngx_rtmp_bit_init_reader(&br, in->buf->pos, in->buf->last);
ngx_rtmp_bit_read(&br, 48);
ctx->avc_profile = (ngx_uint_t) ngx_rtmp_bit_read_8(&br);
ctx->avc_compat = (ngx_uint_t) ngx_rtmp_bit_read_8(&br);
ctx->avc_level = (ngx_uint_t) ngx_rtmp_bit_read_8(&br);
/* nal bytes */
ctx->avc_nal_bytes = (ngx_uint_t) ((ngx_rtmp_bit_read_8(&br) & 0x03) + 1);
/* nnals */
if ((ngx_rtmp_bit_read_8(&br) & 0x1f) == 0) {
return;
}
/* nal size */
ngx_rtmp_bit_read(&br, 16);
/* nal type */
if (ngx_rtmp_bit_read_8(&br) != 0x67) {
return;
}
/* SPS */
/* profile idc */
profile_idc = (ngx_uint_t) ngx_rtmp_bit_read(&br, 8);
/* flags */
ngx_rtmp_bit_read(&br, 8);
/* level idc */
ngx_rtmp_bit_read(&br, 8);
/* SPS id */
ngx_rtmp_bit_read_golomb(&br);
if (profile_idc == 100 || profile_idc == 110 ||
profile_idc == 122 || profile_idc == 244 || profile_idc == 44 ||
profile_idc == 83 || profile_idc == 86 || profile_idc == 118)
{
/* chroma format idc */
cf_idc = (ngx_uint_t) ngx_rtmp_bit_read_golomb(&br);
if (cf_idc == 3) {
/* separate color plane */
ngx_rtmp_bit_read(&br, 1);
}
/* bit depth luma - 8 */
ngx_rtmp_bit_read_golomb(&br);
/* bit depth chroma - 8 */
ngx_rtmp_bit_read_golomb(&br);
/* qpprime y zero transform bypass */
ngx_rtmp_bit_read(&br, 1);
/* seq scaling matrix present */
if (ngx_rtmp_bit_read(&br, 1)) {
for (n = 0, cf_n = (cf_idc != 3 ? 8u : 12u); n < cf_n; n++) {
/* seq scaling list present */
if (ngx_rtmp_bit_read(&br, 1)) {
/* scaling list */
if (n < 6) {
sl_size = 16;
} else {
sl_size = 64;
}
sl_last = 8;
sl_next = 8;
for (sl_index = 0; sl_index < sl_size; sl_index++) {
if (sl_next != 0) {
/* convert to signed: (-1)**k+1 * ceil(k/2) */
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;
}
sl_next = (sl_last + sl_delta + 256) % 256;
if (sl_next != 0) {
sl_last = sl_next;
}
}
}
}
}
}
}
/* log2 max frame num */
ngx_rtmp_bit_read_golomb(&br);
/* pic order cnt type */
switch (ngx_rtmp_bit_read_golomb(&br)) {
case 0:
/* max pic order cnt */
ngx_rtmp_bit_read_golomb(&br);
break;
case 1:
/* delta pic order alwys zero */
ngx_rtmp_bit_read(&br, 1);
/* offset for non-ref pic */
ngx_rtmp_bit_read_golomb(&br);
/* offset for top to bottom field */
ngx_rtmp_bit_read_golomb(&br);
/* num ref frames in pic order */
num_ref_frames = (ngx_uint_t) ngx_rtmp_bit_read_golomb(&br);
for (n = 0; n < num_ref_frames; n++) {
/* offset for ref frame */
ngx_rtmp_bit_read_golomb(&br);
}
}
/* num ref frames */
ctx->avc_ref_frames = (ngx_uint_t) ngx_rtmp_bit_read_golomb(&br);
/* gaps in frame num allowed */
ngx_rtmp_bit_read(&br, 1);
/* pic width in mbs - 1 */
width = (ngx_uint_t) ngx_rtmp_bit_read_golomb(&br);
/* pic height in map units - 1 */
height = (ngx_uint_t) ngx_rtmp_bit_read_golomb(&br);
/* frame mbs only flag */
frame_mbs_only = (ngx_uint_t) ngx_rtmp_bit_read(&br, 1);
if (!frame_mbs_only) {
/* mbs adaprive frame field */
ngx_rtmp_bit_read(&br, 1);
}
/* direct 8x8 inference flag */
ngx_rtmp_bit_read(&br, 1);
/* frame cropping */
if (ngx_rtmp_bit_read(&br, 1)) {
crop_left = (ngx_uint_t) ngx_rtmp_bit_read_golomb(&br);
crop_right = (ngx_uint_t) ngx_rtmp_bit_read_golomb(&br);
crop_top = (ngx_uint_t) ngx_rtmp_bit_read_golomb(&br);
crop_bottom = (ngx_uint_t) ngx_rtmp_bit_read_golomb(&br);
} else {
crop_left = 0;
crop_right = 0;
crop_top = 0;
crop_bottom = 0;
}
ctx->width = (width + 1) * 16 - (crop_left + crop_right) * 2;
ctx->height = (2 - frame_mbs_only) * (height + 1) * 16 -
(crop_top + crop_bottom) * 2;
ngx_log_debug7(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"codec: avc header "
"profile=%ui, compat=%ui, level=%ui, "
"nal_bytes=%ui, ref_frames=%ui, width=%ui, height=%ui",
ctx->avc_profile, ctx->avc_compat, ctx->avc_level,
ctx->avc_nal_bytes, ctx->avc_ref_frames,
ctx->width, ctx->height);
}
#if (NGX_DEBUG)
static void
ngx_rtmp_codec_dump_header(ngx_rtmp_session_t *s, const char *type,
ngx_chain_t *in)
{
u_char buf[256], *p, *pp;
u_char hex[] = "0123456789abcdef";
for (pp = buf, p = in->buf->pos;
p < in->buf->last && pp < buf + sizeof(buf) - 1;
++p)
{
*pp++ = hex[*p >> 4];
*pp++ = hex[*p & 0x0f];
}
*pp = 0;
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"codec: %s header %s", type, buf);
}
#endif
static ngx_int_t
ngx_rtmp_codec_update_meta(ngx_rtmp_session_t *s)
ngx_rtmp_codec_reconstruct_meta(ngx_rtmp_session_t *s)
{
ngx_rtmp_codec_ctx_t *ctx;
ngx_rtmp_core_srv_conf_t *cscf;
ngx_int_t rc;
ngx_rtmp_header_t h;
static struct {
double width;
@ -289,6 +595,7 @@ ngx_rtmp_codec_update_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;
@ -300,49 +607,53 @@ ngx_rtmp_codec_update_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_RTMP_AMF_NUMBER,
ngx_string("width"),
&v.width, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("height"),
&v.height, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("displayWidth"),
&v.width, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("displayHeight"),
&v.height, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("duration"),
&v.duration, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("framerate"),
&v.frame_rate, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("fps"),
&v.frame_rate, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("videodatarate"),
&v.video_data_rate, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ 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 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("audiodatarate"),
&v.audio_data_rate, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("audiocodecid"),
&v.audio_codec_id, 0 },
@ -357,11 +668,11 @@ ngx_rtmp_codec_update_meta(ngx_rtmp_session_t *s)
static ngx_rtmp_amf_elt_t out_elts[] = {
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
"onMetaData", 0 },
{ NGX_RTMP_AMF_OBJECT,
{ NGX_RTMP_AMF_OBJECT,
ngx_null_string,
out_inf, sizeof(out_inf) },
};
@ -383,22 +694,61 @@ ngx_rtmp_codec_update_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;
ngx_memcpy(v.profile, ctx->profile, sizeof(ctx->profile));
ngx_memcpy(v.level, ctx->level, sizeof(ctx->level));
rc = ngx_rtmp_append_amf(s, &ctx->meta, NULL, out_elts,
rc = ngx_rtmp_append_amf(s, &ctx->meta, NULL, out_elts,
sizeof(out_elts) / sizeof(out_elts[0]));
if (rc != NGX_OK || ctx->meta == NULL) {
return NGX_ERROR;
}
return ngx_rtmp_codec_prepare_meta(s, 0);
}
static ngx_int_t
ngx_rtmp_codec_copy_meta(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
ngx_rtmp_codec_ctx_t *ctx;
ngx_rtmp_core_srv_conf_t *cscf;
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
if (ctx->meta) {
ngx_rtmp_free_shared_chain(cscf, ctx->meta);
}
ctx->meta = ngx_rtmp_append_shared_bufs(cscf, NULL, in);
if (ctx->meta == NULL) {
return NGX_ERROR;
}
return ngx_rtmp_codec_prepare_meta(s, h->timestamp);
}
static ngx_int_t
ngx_rtmp_codec_prepare_meta(ngx_rtmp_session_t *s, uint32_t timestamp)
{
ngx_rtmp_header_t h;
ngx_rtmp_codec_ctx_t *ctx;
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
ngx_memzero(&h, sizeof(h));
h.csid = NGX_RTMP_CSID_AMF;
h.msid = NGX_RTMP_MSID;
h.type = NGX_RTMP_MSG_AMF_META;
h.timestamp = timestamp;
ngx_rtmp_prepare_message(s, &h, NULL, ctx->meta);
ctx->meta_version = ngx_rtmp_codec_get_next_version();
@ -407,10 +757,11 @@ ngx_rtmp_codec_update_meta(ngx_rtmp_session_t *s)
}
static ngx_int_t
ngx_rtmp_codec_meta_data(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
static ngx_int_t
ngx_rtmp_codec_meta_data(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
ngx_rtmp_codec_app_conf_t *cacf;
ngx_rtmp_codec_ctx_t *ctx;
ngx_uint_t skip;
@ -420,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;
@ -453,39 +805,43 @@ ngx_rtmp_codec_meta_data(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
static ngx_rtmp_amf_elt_t in_inf[] = {
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("width"),
&v.width, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("height"),
&v.height, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("duration"),
&v.duration, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("framerate"),
&v.frame_rate, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("fps"),
&v.frame_rate, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("videodatarate"),
&v.video_data_rate, 0 },
{ NGX_RTMP_AMF_VARIANT,
{ 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) },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("audiodatarate"),
&v.audio_data_rate, 0 },
{ NGX_RTMP_AMF_VARIANT,
{ NGX_RTMP_AMF_VARIANT,
ngx_string("audiocodecid"),
in_audio_codec_id, sizeof(in_audio_codec_id) },
@ -500,15 +856,18 @@ 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 },
{ NGX_RTMP_AMF_OBJECT,
{ NGX_RTMP_AMF_OBJECT,
ngx_null_string,
in_inf, sizeof(in_inf) },
};
cacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_codec_module);
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
if (ctx == NULL) {
ctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_rtmp_codec_ctx_t));
@ -517,50 +876,100 @@ ngx_rtmp_codec_meta_data(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_memzero(&v, sizeof(v));
/* use -1 as a sign of unchanged data;
* 0 is a valid value for uncompressed audio */
v.audio_codec_id_n = -1;
/* use -1 as a sign of unchanged data */
v.width = -1;
v.height = -1;
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,
sizeof(in_elts) / sizeof(in_elts[0]) - skip))
if (ngx_rtmp_receive_amf(s, in, in_elts + skip,
sizeof(in_elts) / sizeof(in_elts[0]) - skip))
{
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"codec: error parsing data frame");
return NGX_OK;
}
ctx->width = (ngx_uint_t) v.width;
ctx->height = (ngx_uint_t) v.height;
ctx->duration = (ngx_uint_t) v.duration;
ctx->frame_rate = (ngx_uint_t) v.frame_rate;
ctx->video_data_rate = (ngx_uint_t) v.video_data_rate;
ctx->video_codec_id = (ngx_uint_t) v.video_codec_id_n;
ctx->audio_data_rate = (ngx_uint_t) v.audio_data_rate;
ctx->audio_codec_id = (v.audio_codec_id_n == -1
? 0 : v.audio_codec_id_n == 0
if (v.width != -1) ctx->width = (ngx_uint_t) v.width;
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 = 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 = 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);
ngx_memcpy(ctx->profile, v.profile, sizeof(v.profile));
ngx_memcpy(ctx->level, v.level, sizeof(v.level));
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_RTMP, s->connection->log, 0,
"codec: data frame: "
"width=%ui height=%ui duration=%ui frame_rate=%ui "
"width=%ui height=%ui duration=%.3f frame_rate=%.3f "
"video=%s (%ui) audio=%s (%ui)",
ctx->width, ctx->height, ctx->duration, ctx->frame_rate,
ngx_rtmp_get_video_codec_name(ctx->video_codec_id),
ngx_rtmp_get_video_codec_name(ctx->video_codec_id),
ctx->video_codec_id,
ngx_rtmp_get_audio_codec_name(ctx->audio_codec_id),
ngx_rtmp_get_audio_codec_name(ctx->audio_codec_id),
ctx->audio_codec_id);
ngx_rtmp_codec_update_meta(s);
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);
case NGX_RTMP_CODEC_META_COPY:
return ngx_rtmp_codec_copy_meta(s, h, in);
}
/* NGX_RTMP_CODEC_META_OFF */
return NGX_OK;
}
static void *
ngx_rtmp_codec_create_app_conf(ngx_conf_t *cf)
{
ngx_rtmp_codec_app_conf_t *cacf;
cacf = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_codec_app_conf_t));
if (cacf == NULL) {
return NULL;
}
cacf->meta = NGX_CONF_UNSET_UINT;
return cacf;
}
static char *
ngx_rtmp_codec_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_rtmp_codec_app_conf_t *prev = parent;
ngx_rtmp_codec_app_conf_t *conf = child;
ngx_conf_merge_uint_value(conf->meta, prev->meta, NGX_RTMP_CODEC_META_ON);
return NGX_CONF_OK;
}
static ngx_int_t
ngx_rtmp_codec_postconfiguration(ngx_conf_t *cf)
{
@ -587,6 +996,14 @@ 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) {
return NGX_ERROR;
}
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

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -51,13 +52,22 @@ u_char * ngx_rtmp_get_video_codec_name(ngx_uint_t id);
typedef struct {
ngx_uint_t width;
ngx_uint_t height;
ngx_uint_t duration;
ngx_uint_t frame_rate;
ngx_uint_t video_data_rate;
double duration;
double frame_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_sample_rate;
ngx_uint_t aac_profile;
ngx_uint_t aac_chan_conf;
ngx_uint_t aac_sbr;
ngx_uint_t aac_ps;
ngx_uint_t avc_profile;
ngx_uint_t avc_compat;
ngx_uint_t avc_level;
ngx_uint_t avc_nal_bytes;
ngx_uint_t avc_ref_frames;
ngx_uint_t sample_rate; /* 5512, 11025, 22050, 44100 */
ngx_uint_t sample_size; /* 1=8bit, 2=16bit */
ngx_uint_t audio_channels; /* 1, 2 */

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -13,26 +14,34 @@
static char *ngx_rtmp_control(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static void * ngx_rtmp_control_create_loc_conf(ngx_conf_t *cf);
static char * ngx_rtmp_control_merge_loc_conf(ngx_conf_t *cf,
void *parent, void *child);
static char * ngx_rtmp_control_merge_loc_conf(ngx_conf_t *cf,
void *parent, void *child);
typedef struct {
ngx_rtmp_core_main_conf_t *cmcf;
ngx_rtmp_core_srv_conf_t *cscf;
ngx_rtmp_core_app_conf_t *cacf;
} ngx_rtmp_control_core_t;
typedef struct {
ngx_rtmp_live_app_conf_t *lacf;
ngx_rtmp_live_stream_t *ls;
} ngx_rtmp_control_live_t;
typedef const char * (*ngx_rtmp_control_handler_t)(ngx_http_request_t *r,
ngx_rtmp_session_t *);
#define NGX_RTMP_CONTROL_ALL 0xff
#define NGX_RTMP_CONTROL_RECORD 0x01
#define NGX_RTMP_CONTROL_DROP 0x02
#define NGX_RTMP_CONTROL_REDIRECT 0x04
enum {
NGX_RTMP_CONTROL_FILTER_CLIENT = 0,
NGX_RTMP_CONTROL_FILTER_PUBLISHER,
NGX_RTMP_CONTROL_FILTER_SUBSCRIBER
};
typedef struct {
ngx_uint_t count;
ngx_str_t path;
ngx_uint_t filter;
ngx_str_t method;
ngx_array_t sessions; /* ngx_rtmp_session_t * */
} ngx_rtmp_control_ctx_t;
typedef struct {
@ -44,8 +53,9 @@ static ngx_conf_bitmask_t ngx_rtmp_control_masks[] = {
{ ngx_string("all"), NGX_RTMP_CONTROL_ALL },
{ ngx_string("record"), NGX_RTMP_CONTROL_RECORD },
{ ngx_string("drop"), NGX_RTMP_CONTROL_DROP },
{ ngx_string("redirect"), NGX_RTMP_CONTROL_REDIRECT },
{ ngx_null_string, 0 }
};
};
static ngx_command_t ngx_rtmp_control_commands[] = {
@ -62,335 +72,446 @@ static ngx_command_t ngx_rtmp_control_commands[] = {
static ngx_http_module_t ngx_rtmp_control_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_rtmp_control_create_loc_conf, /* create location configuration */
ngx_rtmp_control_merge_loc_conf, /* merge location configuration */
ngx_rtmp_control_create_loc_conf, /* create location configuration */
ngx_rtmp_control_merge_loc_conf, /* merge location configuration */
};
ngx_module_t ngx_rtmp_control_module = {
NGX_MODULE_V1,
&ngx_rtmp_control_module_ctx, /* module context */
ngx_rtmp_control_commands, /* module directives */
NGX_HTTP_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
NGX_MODULE_V1,
&ngx_rtmp_control_module_ctx, /* module context */
ngx_rtmp_control_commands, /* module directives */
NGX_HTTP_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
};
static ngx_int_t
ngx_rtmp_control_output_error(ngx_http_request_t *r, const char *msg)
static const char *
ngx_rtmp_control_record_handler(ngx_http_request_t *r, ngx_rtmp_session_t *s)
{
size_t len;
ngx_buf_t *b;
ngx_chain_t cl;
ngx_int_t rc;
ngx_str_t rec;
ngx_uint_t rn;
ngx_rtmp_control_ctx_t *ctx;
ngx_rtmp_core_app_conf_t *cacf;
ngx_rtmp_record_app_conf_t *racf;
len = ngx_strlen(msg);
cacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_core_module);
racf = cacf->app_conf[ngx_rtmp_record_module.ctx_index];
r->headers_out.status = NGX_HTTP_BAD_REQUEST;
r->headers_out.content_length_n = len;
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NGX_ERROR;
if (ngx_http_arg(r, (u_char *) "rec", sizeof("rec") - 1, &rec) != NGX_OK) {
rec.len = 0;
}
ngx_memzero(&cl, sizeof(cl));
cl.buf = b;
rn = ngx_rtmp_record_find(racf, &rec);
if (rn == NGX_CONF_UNSET_UINT) {
return "Recorder not found";
}
b->start = b->pos = (u_char *) msg;
b->end = b->last = (u_char *) msg + len;
b->memory = 1;
b->last_buf = 1;
ctx = ngx_http_get_module_ctx(r, ngx_rtmp_control_module);
ngx_http_send_header(r);
if (ctx->method.len == sizeof("start") - 1 &&
ngx_strncmp(ctx->method.data, "start", ctx->method.len) == 0)
{
rc = ngx_rtmp_record_open(s, rn, &ctx->path);
return ngx_http_output_filter(r, &cl);
} else if (ctx->method.len == sizeof("stop") - 1 &&
ngx_strncmp(ctx->method.data, "stop", ctx->method.len) == 0)
{
rc = ngx_rtmp_record_close(s, rn, &ctx->path);
} else {
return "Undefined method";
}
if (rc == NGX_ERROR) {
return "Recorder error";
}
return NGX_CONF_OK;
}
static const char *
ngx_rtmp_control_parse_core(ngx_http_request_t *r,
ngx_rtmp_control_core_t *core)
ngx_rtmp_control_drop_handler(ngx_http_request_t *r, ngx_rtmp_session_t *s)
{
ngx_str_t srv, app;
ngx_uint_t sn, n;
ngx_rtmp_core_srv_conf_t **pcscf;
ngx_rtmp_core_app_conf_t **pcacf;
ngx_rtmp_control_ctx_t *ctx;
core->cmcf = ngx_rtmp_core_main_conf;
if (core->cmcf == NULL) {
return "Missing main RTMP conf";
ctx = ngx_http_get_module_ctx(r, ngx_rtmp_control_module);
ngx_rtmp_finalize_session(s);
++ctx->count;
return NGX_CONF_OK;
}
static const char *
ngx_rtmp_control_redirect_handler(ngx_http_request_t *r, ngx_rtmp_session_t *s)
{
ngx_str_t name;
ngx_rtmp_play_t vplay;
ngx_rtmp_publish_t vpublish;
ngx_rtmp_live_ctx_t *lctx;
ngx_rtmp_control_ctx_t *ctx;
ngx_rtmp_close_stream_t vc;
if (ngx_http_arg(r, (u_char *) "newname", sizeof("newname") - 1, &name)
!= NGX_OK)
{
return "newname not specified";
}
/* find server */
sn = 0;
if (name.len >= NGX_RTMP_MAX_NAME) {
name.len = NGX_RTMP_MAX_NAME - 1;
}
ctx = ngx_http_get_module_ctx(r, ngx_rtmp_control_module);
ctx->count++;
ngx_memzero(&vc, sizeof(ngx_rtmp_close_stream_t));
/* close_stream should be synchronous */
ngx_rtmp_close_stream(s, &vc);
lctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_live_module);
if (lctx && lctx->publishing) {
/* publish */
ngx_memzero(&vpublish, sizeof(ngx_rtmp_publish_t));
ngx_memcpy(vpublish.name, name.data, name.len);
ngx_rtmp_cmd_fill_args(vpublish.name, vpublish.args);
if (ngx_rtmp_publish(s, &vpublish) != NGX_OK) {
return "publish failed";
}
} else {
/* play */
ngx_memzero(&vplay, sizeof(ngx_rtmp_play_t));
ngx_memcpy(vplay.name, name.data, name.len);
ngx_rtmp_cmd_fill_args(vplay.name, vplay.args);
if (ngx_rtmp_play(s, &vplay) != NGX_OK) {
return "play failed";
}
}
return NGX_CONF_OK;
}
static const char *
ngx_rtmp_control_walk_session(ngx_http_request_t *r,
ngx_rtmp_live_ctx_t *lctx)
{
ngx_str_t addr, *paddr, clientid;
ngx_rtmp_session_t *s, **ss;
ngx_rtmp_control_ctx_t *ctx;
s = lctx->session;
if (s == NULL || s->connection == NULL) {
return NGX_CONF_OK;
}
if (ngx_http_arg(r, (u_char *) "addr", sizeof("addr") - 1, &addr)
== NGX_OK)
{
paddr = &s->connection->addr_text;
if (paddr->len != addr.len ||
ngx_strncmp(paddr->data, addr.data, addr.len))
{
return NGX_CONF_OK;
}
}
if (ngx_http_arg(r, (u_char *) "clientid", sizeof("clientid") - 1,
&clientid)
== NGX_OK)
{
if (s->connection->number !=
(ngx_uint_t) ngx_atoi(clientid.data, clientid.len))
{
return NGX_CONF_OK;
}
}
ctx = ngx_http_get_module_ctx(r, ngx_rtmp_control_module);
switch (ctx->filter) {
case NGX_RTMP_CONTROL_FILTER_PUBLISHER:
if (!lctx->publishing) {
return NGX_CONF_OK;
}
break;
case NGX_RTMP_CONTROL_FILTER_SUBSCRIBER:
if (lctx->publishing) {
return NGX_CONF_OK;
}
break;
case NGX_RTMP_CONTROL_FILTER_CLIENT:
break;
}
ss = ngx_array_push(&ctx->sessions);
if (ss == NULL) {
return "allocation error";
}
*ss = s;
return NGX_CONF_OK;
}
static const char *
ngx_rtmp_control_walk_stream(ngx_http_request_t *r,
ngx_rtmp_live_stream_t *ls)
{
const char *s;
ngx_rtmp_live_ctx_t *lctx;
for (lctx = ls->ctx; lctx; lctx = lctx->next) {
s = ngx_rtmp_control_walk_session(r, lctx);
if (s != NGX_CONF_OK) {
return s;
}
}
return NGX_CONF_OK;
}
static const char *
ngx_rtmp_control_walk_app(ngx_http_request_t *r,
ngx_rtmp_core_app_conf_t *cacf)
{
size_t len;
ngx_str_t name;
const char *s;
ngx_uint_t n;
ngx_rtmp_live_stream_t *ls;
ngx_rtmp_live_app_conf_t *lacf;
lacf = cacf->app_conf[ngx_rtmp_live_module.ctx_index];
if (ngx_http_arg(r, (u_char *) "name", sizeof("name") - 1, &name) != NGX_OK)
{
for (n = 0; n < (ngx_uint_t) lacf->nbuckets; ++n) {
for (ls = lacf->streams[n]; ls; ls = ls->next) {
s = ngx_rtmp_control_walk_stream(r, ls);
if (s != NGX_CONF_OK) {
return s;
}
}
}
return NGX_CONF_OK;
}
for (ls = lacf->streams[ngx_hash_key(name.data, name.len) % lacf->nbuckets];
ls; ls = ls->next)
{
len = ngx_strlen(ls->name);
if (name.len != len || ngx_strncmp(name.data, ls->name, name.len)) {
continue;
}
s = ngx_rtmp_control_walk_stream(r, ls);
if (s != NGX_CONF_OK) {
return s;
}
}
return NGX_CONF_OK;
}
static const char *
ngx_rtmp_control_walk_server(ngx_http_request_t *r,
ngx_rtmp_core_srv_conf_t *cscf)
{
ngx_str_t app;
ngx_uint_t n;
const char *s;
ngx_rtmp_core_app_conf_t **pcacf;
if (ngx_http_arg(r, (u_char *) "app", sizeof("app") - 1, &app) != NGX_OK) {
app.len = 0;
}
pcacf = cscf->applications.elts;
for (n = 0; n < cscf->applications.nelts; ++n, ++pcacf) {
if (app.len && ((*pcacf)->name.len != app.len ||
ngx_strncmp((*pcacf)->name.data, app.data, app.len)))
{
continue;
}
s = ngx_rtmp_control_walk_app(r, *pcacf);
if (s != NGX_CONF_OK) {
return s;
}
}
return NGX_CONF_OK;
}
static const char *
ngx_rtmp_control_walk(ngx_http_request_t *r, ngx_rtmp_control_handler_t h)
{
ngx_rtmp_core_main_conf_t *cmcf = ngx_rtmp_core_main_conf;
ngx_str_t srv;
ngx_uint_t sn, n;
const char *msg;
ngx_rtmp_session_t **s;
ngx_rtmp_control_ctx_t *ctx;
ngx_rtmp_core_srv_conf_t **pcscf;
sn = 0;
if (ngx_http_arg(r, (u_char *) "srv", sizeof("srv") - 1, &srv) == NGX_OK) {
sn = ngx_atoi(srv.data, srv.len);
}
if (sn >= core->cmcf->servers.nelts) {
if (sn >= cmcf->servers.nelts) {
return "Server index out of range";
}
pcscf = core->cmcf->servers.elts;
pcscf = cmcf->servers.elts;
pcscf += sn;
core->cscf = *pcscf;
/* find application */
if (ngx_http_arg(r, (u_char *) "app", sizeof("app") - 1, &app) != NGX_OK) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"rtmp_control: app not specified");
return "Application not specified";
msg = ngx_rtmp_control_walk_server(r, *pcscf);
if (msg != NGX_CONF_OK) {
return msg;
}
core->cacf = NULL;
ctx = ngx_http_get_module_ctx(r, ngx_rtmp_control_module);
pcacf = core->cscf->applications.elts;
for (n = 0; n < core->cscf->applications.nelts; ++n, ++pcacf) {
if ((*pcacf)->name.len == app.len &&
ngx_strncmp((*pcacf)->name.data, app.data, app.len) == 0)
{
core->cacf = *pcacf;
break;
s = ctx->sessions.elts;
for (n = 0; n < ctx->sessions.nelts; n++) {
msg = h(r, s[n]);
if (msg != NGX_CONF_OK) {
return msg;
}
}
if (core->cacf == NULL) {
return "Application not found";
}
return NGX_CONF_OK;
}
static const char *
ngx_rtmp_control_parse_live(ngx_http_request_t *r,
ngx_rtmp_control_core_t *core,
ngx_rtmp_control_live_t *live)
{
ngx_str_t name;
size_t len;
ngx_memzero(&name, sizeof(name));
ngx_http_arg(r, (u_char *) "name", sizeof("name") - 1, &name);
live->lacf = core->cacf->app_conf[ngx_rtmp_live_module.ctx_index];
/* find live stream by name */
for (live->ls = live->lacf->streams[ngx_hash_key(name.data, name.len) %
live->lacf->nbuckets];
live->ls; live->ls = live->ls->next)
{
len = ngx_strlen(live->ls->name);
if (name.len == len && ngx_strncmp(name.data, live->ls->name, name.len)
== 0)
{
break;
}
}
if (live->ls == NULL) {
return "Live stream not found";
}
return NGX_CONF_OK;
}
/* /record arguments:
* srv - server index (optional)
* app - application name
* name - stream name
* rec - recorder name
*/
static ngx_int_t
ngx_rtmp_control_record(ngx_http_request_t *r, ngx_str_t *method)
{
ngx_rtmp_control_core_t core;
ngx_rtmp_control_live_t live;
ngx_rtmp_record_app_conf_t *racf;
ngx_rtmp_live_ctx_t *lctx;
ngx_rtmp_session_t *s;
ngx_chain_t cl;
ngx_uint_t rn;
ngx_str_t rec, path;
ngx_buf_t *b;
ngx_int_t rc;
const char *msg;
ngx_buf_t *b;
const char *msg;
ngx_chain_t cl;
ngx_rtmp_control_ctx_t *ctx;
msg = ngx_rtmp_control_parse_core(r, &core);
ctx = ngx_http_get_module_ctx(r, ngx_rtmp_control_module);
ctx->filter = NGX_RTMP_CONTROL_FILTER_PUBLISHER;
msg = ngx_rtmp_control_walk(r, ngx_rtmp_control_record_handler);
if (msg != NGX_CONF_OK) {
goto error;
}
msg = ngx_rtmp_control_parse_live(r, &core, &live);
if (msg != NGX_CONF_OK) {
goto error;
if (ctx->path.len == 0) {
return NGX_HTTP_NO_CONTENT;
}
/* find publisher context */
for (lctx = live.ls->ctx; lctx; lctx = lctx->next) {
if (lctx->publishing) {
break;
}
}
if (lctx == NULL) {
msg = "No publisher";
goto error;
}
s = lctx->session;
/* find recorder */
ngx_memzero(&rec, sizeof(rec));
ngx_http_arg(r, (u_char *) "rec", sizeof("rec") - 1, &rec);
racf = core.cacf->app_conf[ngx_rtmp_record_module.ctx_index];
rn = ngx_rtmp_record_find(racf, &rec);
if (rn == NGX_CONF_UNSET_UINT) {
msg = "Recorder not found";
goto error;
}
/* call the method */
ngx_memzero(&path, sizeof(path));
if (method->len == sizeof("start") - 1 &&
ngx_strncmp(method->data, "start", method->len) == 0)
{
rc = ngx_rtmp_record_open(s, rn, &path);
} else if (method->len == sizeof("stop") - 1 &&
ngx_strncmp(method->data, "stop", method->len) == 0)
{
rc = ngx_rtmp_record_close(s, rn, &path);
} else {
msg = "Undefined method";
goto error;
}
if (rc == NGX_ERROR) {
msg = "Recorder error";
goto error;
}
if (rc == NGX_AGAIN) {
/* already opened/closed */
ngx_str_null(&path);
r->header_only = 1;
}
/* output record path */
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = path.len;
r->headers_out.content_length_n = ctx->path.len;
b = ngx_create_temp_buf(r->pool, path.len);
b = ngx_create_temp_buf(r->pool, ctx->path.len);
if (b == NULL) {
return NGX_ERROR;
goto error;
}
ngx_memzero(&cl, sizeof(cl));
cl.buf = b;
b->last = ngx_cpymem(b->pos, path.data, path.len);
b->last = ngx_cpymem(b->pos, ctx->path.data, ctx->path.len);
b->last_buf = 1;
ngx_http_send_header(r);
return ngx_http_output_filter(r, &cl);
error:
return ngx_rtmp_control_output_error(r, msg);
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
static ngx_int_t
ngx_rtmp_control_drop(ngx_http_request_t *r, ngx_str_t *method)
{
ngx_rtmp_control_core_t core;
ngx_rtmp_control_live_t live;
ngx_rtmp_live_ctx_t *lctx;
ngx_str_t addr, *paddr;
const char *msg;
ngx_uint_t ndropped;
size_t len;
u_char *p;
ngx_buf_t *b;
ngx_chain_t cl;
size_t len;
u_char *p;
ngx_buf_t *b;
ngx_chain_t cl;
const char *msg;
ngx_rtmp_control_ctx_t *ctx;
msg = ngx_rtmp_control_parse_core(r, &core);
if (msg != NGX_CONF_OK) {
goto error;
}
ctx = ngx_http_get_module_ctx(r, ngx_rtmp_control_module);
msg = ngx_rtmp_control_parse_live(r, &core, &live);
if (msg != NGX_CONF_OK) {
goto error;
}
ndropped = 0;
if (method->len == sizeof("publisher") - 1 &&
ngx_strncmp(method->data, "publisher", method->len) == 0)
if (ctx->method.len == sizeof("publisher") - 1 &&
ngx_memcmp(ctx->method.data, "publisher", ctx->method.len) == 0)
{
for (lctx = live.ls->ctx; lctx; lctx = lctx->next) {
if (lctx->publishing) {
ngx_rtmp_finalize_session(lctx->session);
++ndropped;
break;
}
}
ctx->filter = NGX_RTMP_CONTROL_FILTER_PUBLISHER;
} else if (ctx->method.len == sizeof("subscriber") - 1 &&
ngx_memcmp(ctx->method.data, "subscriber", ctx->method.len)
== 0)
{
ctx->filter = NGX_RTMP_CONTROL_FILTER_SUBSCRIBER;
} else if (method->len == sizeof("client") - 1 &&
ngx_strncmp(method->data, "client", method->len) == 0)
ngx_memcmp(ctx->method.data, "client", ctx->method.len) == 0)
{
ngx_memzero(&addr, sizeof(addr));
ngx_http_arg(r, (u_char *) "addr", sizeof("addr") - 1, &addr);
for (lctx = live.ls->ctx; lctx; lctx = lctx->next) {
if (addr.len && lctx->session && lctx->session->connection) {
paddr = &lctx->session->connection->addr_text;
if (paddr->len != addr.len ||
ngx_strncmp(paddr->data, addr.data, addr.len))
{
continue;
}
}
ngx_rtmp_finalize_session(lctx->session);
++ndropped;
}
ctx->filter = NGX_RTMP_CONTROL_FILTER_CLIENT;
} else {
msg = "Undefined method";
msg = "Undefined filter";
goto error;
}
/* output ndropped */
msg = ngx_rtmp_control_walk(r, ngx_rtmp_control_drop_handler);
if (msg != NGX_CONF_OK) {
goto error;
}
/* output count */
len = NGX_INT_T_LEN;
@ -399,14 +520,14 @@ ngx_rtmp_control_drop(ngx_http_request_t *r, ngx_str_t *method)
return NGX_ERROR;
}
len = (size_t) (ngx_snprintf(p, len, "%ui", ndropped) - p);
len = (size_t) (ngx_snprintf(p, len, "%ui", ctx->count) - p);
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = len;
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NGX_ERROR;
goto error;
}
b->start = b->pos = p;
@ -422,17 +543,92 @@ ngx_rtmp_control_drop(ngx_http_request_t *r, ngx_str_t *method)
return ngx_http_output_filter(r, &cl);
error:
return ngx_rtmp_control_output_error(r, msg);
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
static ngx_int_t
ngx_rtmp_control_redirect(ngx_http_request_t *r, ngx_str_t *method)
{
size_t len;
u_char *p;
ngx_buf_t *b;
ngx_chain_t cl;
const char *msg;
ngx_rtmp_control_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_rtmp_control_module);
if (ctx->method.len == sizeof("publisher") - 1 &&
ngx_memcmp(ctx->method.data, "publisher", ctx->method.len) == 0)
{
ctx->filter = NGX_RTMP_CONTROL_FILTER_PUBLISHER;
} else if (ctx->method.len == sizeof("subscriber") - 1 &&
ngx_memcmp(ctx->method.data, "subscriber", ctx->method.len)
== 0)
{
ctx->filter = NGX_RTMP_CONTROL_FILTER_SUBSCRIBER;
} else if (ctx->method.len == sizeof("client") - 1 &&
ngx_memcmp(ctx->method.data, "client", ctx->method.len) == 0)
{
ctx->filter = NGX_RTMP_CONTROL_FILTER_CLIENT;
} else {
msg = "Undefined filter";
goto error;
}
msg = ngx_rtmp_control_walk(r, ngx_rtmp_control_redirect_handler);
if (msg != NGX_CONF_OK) {
goto error;
}
/* output count */
len = NGX_INT_T_LEN;
p = ngx_palloc(r->connection->pool, len);
if (p == NULL) {
goto error;
}
len = (size_t) (ngx_snprintf(p, len, "%ui", ctx->count) - p);
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = len;
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
goto error;
}
b->start = b->pos = p;
b->end = b->last = p + len;
b->temporary = 1;
b->last_buf = 1;
ngx_memzero(&cl, sizeof(cl));
cl.buf = b;
ngx_http_send_header(r);
return ngx_http_output_filter(r, &cl);
error:
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
static ngx_int_t
ngx_rtmp_control_handler(ngx_http_request_t *r)
{
ngx_rtmp_control_loc_conf_t *llcf;
ngx_str_t section, method;
u_char *p;
ngx_uint_t n;
u_char *p;
ngx_str_t section, method;
ngx_uint_t n;
ngx_rtmp_control_ctx_t *ctx;
ngx_rtmp_control_loc_conf_t *llcf;
llcf = ngx_http_get_module_loc_conf(r, ngx_rtmp_control_module);
if (llcf->control == 0) {
@ -440,8 +636,9 @@ ngx_rtmp_control_handler(ngx_http_request_t *r)
}
/* uri format: .../section/method?args */
ngx_memzero(&section, sizeof(section));
ngx_memzero(&method, sizeof(method));
ngx_str_null(&section);
ngx_str_null(&method);
for (n = r->uri.len; n; --n) {
p = &r->uri.data[n - 1];
@ -464,6 +661,18 @@ ngx_rtmp_control_handler(ngx_http_request_t *r)
"rtmp_control: section='%V' method='%V'",
&section, &method);
ctx = ngx_pcalloc(r->pool, sizeof(ngx_rtmp_control_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
ngx_http_set_ctx(r, ctx, ngx_rtmp_control_module);
if (ngx_array_init(&ctx->sessions, r->pool, 1, sizeof(void *)) != NGX_OK) {
return NGX_ERROR;
}
ctx->method = method;
#define NGX_RTMP_CONTROL_SECTION(flag, secname) \
if (llcf->control & NGX_RTMP_CONTROL_##flag && \
@ -475,10 +684,10 @@ ngx_rtmp_control_handler(ngx_http_request_t *r)
NGX_RTMP_CONTROL_SECTION(RECORD, record);
NGX_RTMP_CONTROL_SECTION(DROP, drop);
NGX_RTMP_CONTROL_SECTION(REDIRECT, redirect);
#undef NGX_RTMP_CONTROL_SECTION
return NGX_DECLINED;
}
@ -486,7 +695,7 @@ ngx_rtmp_control_handler(ngx_http_request_t *r)
static void *
ngx_rtmp_control_create_loc_conf(ngx_conf_t *cf)
{
ngx_rtmp_control_loc_conf_t *conf;
ngx_rtmp_control_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_control_loc_conf_t));
if (conf == NULL) {
@ -502,8 +711,8 @@ ngx_rtmp_control_create_loc_conf(ngx_conf_t *cf)
static char *
ngx_rtmp_control_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_rtmp_control_loc_conf_t *prev = parent;
ngx_rtmp_control_loc_conf_t *conf = child;
ngx_rtmp_control_loc_conf_t *prev = parent;
ngx_rtmp_control_loc_conf_t *conf = child;
ngx_conf_merge_bitmask_value(conf->control, prev->control, 0);

View file

@ -1,6 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -30,7 +30,7 @@ ngx_rtmp_core_main_conf_t *ngx_rtmp_core_main_conf;
static ngx_conf_deprecated_t ngx_conf_deprecated_so_keepalive = {
ngx_conf_deprecated, "so_keepalive",
ngx_conf_deprecated, "so_keepalive",
"so_keepalive\" parameter of the \"listen"
};
@ -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,
@ -150,6 +150,13 @@ static ngx_command_t ngx_rtmp_core_commands[] = {
offsetof(ngx_rtmp_core_srv_conf_t, publish_time_fix),
NULL },
{ ngx_string("buflen"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_RTMP_SRV_CONF_OFFSET,
offsetof(ngx_rtmp_core_srv_conf_t, buflen),
NULL },
ngx_null_command
};
@ -240,6 +247,7 @@ ngx_rtmp_core_create_srv_conf(ngx_conf_t *cf)
conf->out_cork = NGX_CONF_UNSET_SIZE;
conf->play_time_fix = NGX_CONF_UNSET;
conf->publish_time_fix = NGX_CONF_UNSET;
conf->buflen = NGX_CONF_UNSET_MSEC;
conf->busy = NGX_CONF_UNSET;
return conf;
@ -260,13 +268,14 @@ ngx_rtmp_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->max_streams, prev->max_streams, 32);
ngx_conf_merge_value(conf->chunk_size, prev->chunk_size, 4096);
ngx_conf_merge_uint_value(conf->ack_window, prev->ack_window, 5000000);
ngx_conf_merge_size_value(conf->max_message, prev->max_message,
ngx_conf_merge_size_value(conf->max_message, prev->max_message,
1 * 1024 * 1024);
ngx_conf_merge_size_value(conf->out_queue, prev->out_queue, 256);
ngx_conf_merge_size_value(conf->out_cork, prev->out_cork,
ngx_conf_merge_size_value(conf->out_cork, prev->out_cork,
conf->out_queue / 8);
ngx_conf_merge_value(conf->play_time_fix, prev->play_time_fix, 1);
ngx_conf_merge_value(conf->publish_time_fix, prev->publish_time_fix, 1);
ngx_conf_merge_msec_value(conf->buflen, prev->buflen, 1000);
ngx_conf_merge_value(conf->busy, prev->busy, 0);
if (prev->pool == NULL) {
@ -323,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;
@ -348,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);
@ -361,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) {
@ -370,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;
}
}
@ -410,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;
@ -429,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;
}
}
@ -479,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;
@ -536,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;
}
@ -556,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) {
@ -577,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;
@ -710,6 +731,11 @@ ngx_rtmp_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
#endif
}
if (ngx_strcmp(value[i].data, "proxy_protocol") == 0) {
ls->proxy_protocol = 1;
continue;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the invalid \"%V\" parameter", &value[i]);
return NGX_CONF_ERROR;

View file

@ -1,589 +0,0 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include "ngx_rtmp.h"
#include "ngx_rtmp_eval.h"
#include "ngx_rtmp_cmd_module.h"
#include "ngx_rtmp_record_module.h"
#include <stdlib.h>
#ifdef NGX_LINUX
#include <unistd.h>
#endif
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_done_pt next_record_done;
static char *ngx_rtmp_enotify_on_event(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_int_t ngx_rtmp_enotify_postconfiguration(ngx_conf_t *cf);
static void * ngx_rtmp_enotify_create_app_conf(ngx_conf_t *cf);
static char * ngx_rtmp_enotify_merge_app_conf(ngx_conf_t *cf,
void *parent, void *child);
#define NGX_RTMP_ENOTIFY_PUBLISHING 0x01
#define NGX_RTMP_ENOTIFY_PLAYING 0x02
enum {
NGX_RTMP_ENOTIFY_PUBLISH,
NGX_RTMP_ENOTIFY_PLAY,
NGX_RTMP_ENOTIFY_PUBLISH_DONE,
NGX_RTMP_ENOTIFY_PLAY_DONE,
NGX_RTMP_ENOTIFY_RECORD_DONE,
NGX_RTMP_ENOTIFY_MAX
};
typedef struct {
ngx_str_t cmd;
ngx_array_t args; /* ngx_str_t */
} ngx_rtmp_enotify_conf_t;
typedef struct {
ngx_rtmp_enotify_conf_t *event[NGX_RTMP_ENOTIFY_MAX];
ngx_flag_t active;
} ngx_rtmp_enotify_app_conf_t;
typedef struct {
ngx_uint_t flags;
u_char name[NGX_RTMP_MAX_NAME];
u_char args[NGX_RTMP_MAX_ARGS];
ngx_str_t path;
ngx_str_t recorder;
} ngx_rtmp_enotify_ctx_t;
static ngx_command_t ngx_rtmp_enotify_commands[] = {
{ ngx_string("exec_publish"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_1MORE,
ngx_rtmp_enotify_on_event,
NGX_RTMP_APP_CONF_OFFSET,
0,
NULL },
{ ngx_string("exec_play"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_1MORE,
ngx_rtmp_enotify_on_event,
NGX_RTMP_APP_CONF_OFFSET,
0,
NULL },
{ ngx_string("exec_publish_done"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_1MORE,
ngx_rtmp_enotify_on_event,
NGX_RTMP_APP_CONF_OFFSET,
0,
NULL },
{ ngx_string("exec_play_done"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_1MORE,
ngx_rtmp_enotify_on_event,
NGX_RTMP_APP_CONF_OFFSET,
0,
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,
ngx_rtmp_enotify_on_event,
NGX_RTMP_APP_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
static ngx_rtmp_module_t ngx_rtmp_enotify_module_ctx = {
NULL, /* preconfiguration */
ngx_rtmp_enotify_postconfiguration, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_rtmp_enotify_create_app_conf, /* create app configuration */
ngx_rtmp_enotify_merge_app_conf /* merge app configuration */
};
ngx_module_t ngx_rtmp_enotify_module = {
NGX_MODULE_V1,
&ngx_rtmp_enotify_module_ctx, /* module context */
ngx_rtmp_enotify_commands, /* 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
};
static void
ngx_rtmp_enotify_eval_astr(ngx_rtmp_session_t *s, ngx_rtmp_eval_t *e,
ngx_str_t *ret)
{
ngx_rtmp_enotify_ctx_t *ctx;
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_enotify_module);
if (ctx == NULL) {
ret->len = 0;
return;
}
ret->data = (u_char *) ctx + e->offset;
ret->len = ngx_strlen(ret->data);
}
static void
ngx_rtmp_enotify_eval_str(ngx_rtmp_session_t *s, ngx_rtmp_eval_t *e,
ngx_str_t *ret)
{
ngx_rtmp_enotify_ctx_t *ctx;
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_enotify_module);
if (ctx == NULL) {
ret->len = 0;
return;
}
*ret = *(ngx_str_t *) ((u_char *) ctx + e->offset);
}
static ngx_rtmp_eval_t ngx_rtmp_enotify_eval[] = {
{ ngx_string("name"),
ngx_rtmp_enotify_eval_astr,
offsetof(ngx_rtmp_enotify_ctx_t, name) },
{ ngx_string("args"),
ngx_rtmp_enotify_eval_astr,
offsetof(ngx_rtmp_enotify_ctx_t, args) },
{ ngx_string("path"),
ngx_rtmp_enotify_eval_str,
offsetof(ngx_rtmp_enotify_ctx_t, path) },
{ ngx_string("recorder"),
ngx_rtmp_enotify_eval_str,
offsetof(ngx_rtmp_enotify_ctx_t, recorder) },
ngx_rtmp_null_eval
};
static ngx_rtmp_eval_t * ngx_rtmp_enotify_eval_p[] = {
ngx_rtmp_eval_session,
ngx_rtmp_enotify_eval,
NULL
};
static void *
ngx_rtmp_enotify_create_app_conf(ngx_conf_t *cf)
{
ngx_rtmp_enotify_app_conf_t *enacf;
ngx_uint_t n;
enacf = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_enotify_app_conf_t));
if (enacf == NULL) {
return NULL;
}
for (n = 0; n < NGX_RTMP_ENOTIFY_MAX; ++n) {
enacf->event[n] = NGX_CONF_UNSET_PTR;
}
return enacf;
}
static char *
ngx_rtmp_enotify_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_rtmp_enotify_app_conf_t *prev = parent;
ngx_rtmp_enotify_app_conf_t *conf = child;
ngx_uint_t n;
for (n = 0; n < NGX_RTMP_ENOTIFY_MAX; ++n) {
ngx_conf_merge_ptr_value(conf->event[n], prev->event[n], NULL);
if (conf->event[n]) {
conf->active = 1;
}
}
if (conf->active) {
prev->active = 1;
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_rtmp_enotify_exec(ngx_rtmp_session_t *s, ngx_rtmp_enotify_conf_t *ec)
{
#if !(NGX_WIN32)
int pid, fd, maxfd;
ngx_str_t a, *arg_in;
char **args, **arg_out;
ngx_uint_t n;
pid = fork();
switch (pid) {
case -1:
ngx_log_error(NGX_LOG_INFO, s->connection->log, ngx_errno,
"enotify: fork failed");
return NGX_ERROR;
case 0:
/* child */
/* close all descriptors */
maxfd = sysconf(_SC_OPEN_MAX);
for (fd = 0; fd < maxfd; ++fd) {
close(fd);
}
fd = open("/dev/null", O_RDWR);
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
args = ngx_palloc(s->connection->pool,
(ec->args.nelts + 2) * sizeof(char *));
if (args == NULL) {
exit(1);
}
arg_in = ec->args.elts;
arg_out = args;
*arg_out++ = (char *) ec->cmd.data;
for (n = 0; n < ec->args.nelts; ++n, ++arg_in) {
ngx_rtmp_eval(s, arg_in, ngx_rtmp_enotify_eval_p, &a);
if (ngx_rtmp_eval_streams(&a) != NGX_DONE) {
continue;
}
*arg_out++ = (char *) a.data;
}
*arg_out = NULL;
if (execvp((char *) ec->cmd.data, args) == -1) {
exit(1);
}
break;
default:
/* parent */
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"enotify: child '%V' started pid=%ui",
&ec->cmd, (ngx_uint_t)pid);
break;
}
#endif /* NGX_WIN32 */
return NGX_OK;
}
static void
ngx_rtmp_enotify_init(ngx_rtmp_session_t *s,
u_char name[NGX_RTMP_MAX_NAME], u_char args[NGX_RTMP_MAX_ARGS],
ngx_uint_t flags)
{
ngx_rtmp_enotify_ctx_t *ctx;
ngx_rtmp_enotify_app_conf_t *enacf;
enacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_enotify_module);
if (!enacf->active) {
return;
}
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_enotify_module);
if (ctx == NULL) {
ctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_rtmp_enotify_ctx_t));
if (ctx == NULL) {
return;
}
ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_enotify_module);
}
ngx_memcpy(ctx->name, name, NGX_RTMP_MAX_NAME);
ngx_memcpy(ctx->args, args, NGX_RTMP_MAX_ARGS);
ctx->flags |= flags;
}
static ngx_int_t
ngx_rtmp_enotify_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
{
ngx_rtmp_enotify_app_conf_t *enacf;
ngx_rtmp_enotify_conf_t *ec;
if (s->auto_pushed) {
goto next;
}
enacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_enotify_module);
if (enacf == NULL) {
goto next;
}
ngx_rtmp_enotify_init(s, v->name, v->args, NGX_RTMP_ENOTIFY_PUBLISHING);
ec = enacf->event[NGX_RTMP_ENOTIFY_PUBLISH];
if (ec == NULL) {
goto next;
}
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"enotify: publish '%V'", &ec->cmd);
ngx_rtmp_enotify_exec(s, ec);
next:
return next_publish(s, v);
}
static ngx_int_t
ngx_rtmp_enotify_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
{
ngx_rtmp_enotify_app_conf_t *enacf;
ngx_rtmp_enotify_conf_t *ec;
if (s->auto_pushed) {
goto next;
}
enacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_enotify_module);
if (enacf == NULL) {
goto next;
}
ngx_rtmp_enotify_init(s, v->name, v->args, NGX_RTMP_ENOTIFY_PLAYING);
ec = enacf->event[NGX_RTMP_ENOTIFY_PLAY];
if (ec == NULL) {
goto next;
}
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"enotify: play '%V'", &ec->cmd);
ngx_rtmp_enotify_exec(s, ec);
next:
return next_play(s, v);
}
static ngx_int_t
ngx_rtmp_enotify_close_stream(ngx_rtmp_session_t *s, ngx_rtmp_close_stream_t
*v)
{
ngx_rtmp_enotify_ctx_t *ctx;
ngx_rtmp_enotify_app_conf_t *enacf;
ngx_rtmp_enotify_conf_t *ec;
if (s->auto_pushed) {
goto next;
}
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_enotify_module);
if (ctx == NULL) {
goto next;
}
enacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_enotify_module);
if (enacf == NULL) {
goto next;
}
if (enacf->event[NGX_RTMP_ENOTIFY_PUBLISH_DONE] &&
(ctx->flags & NGX_RTMP_ENOTIFY_PUBLISHING))
{
ec = enacf->event[NGX_RTMP_ENOTIFY_PUBLISH_DONE];
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"enotify: publish_done '%V'", &ec->cmd);
ngx_rtmp_enotify_exec(s, ec);
}
if (enacf->event[NGX_RTMP_ENOTIFY_PLAY_DONE] &&
(ctx->flags & NGX_RTMP_ENOTIFY_PLAYING))
{
ec = enacf->event[NGX_RTMP_ENOTIFY_PLAY_DONE];
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"enotify: play_done '%V'", &ec->cmd);
ngx_rtmp_enotify_exec(s, ec);
}
ctx->flags = 0;
next:
return next_close_stream(s, v);
}
static ngx_int_t
ngx_rtmp_enotify_record_done(ngx_rtmp_session_t *s, ngx_rtmp_record_done_t *v)
{
ngx_rtmp_enotify_app_conf_t *enacf;
ngx_rtmp_enotify_conf_t *ec;
ngx_rtmp_enotify_ctx_t *ctx;
if (s->auto_pushed) {
goto next;
}
enacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_enotify_module);
if (enacf == NULL || enacf->event[NGX_RTMP_ENOTIFY_RECORD_DONE] == NULL) {
goto next;
}
ec = enacf->event[NGX_RTMP_ENOTIFY_RECORD_DONE];
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"enotify: record_done %V recorder=%V path='%V'",
&ec->cmd, &v->recorder, &v->path);
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_enotify_module);
if (ctx == NULL) {
goto next;
}
ctx->recorder = v->recorder;
ctx->path = v->path;
ngx_rtmp_enotify_exec(s, ec);
next:
return next_record_done(s, v);
}
static char *
ngx_rtmp_enotify_on_event(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_rtmp_enotify_app_conf_t *enacf;
ngx_rtmp_enotify_conf_t *ec;
ngx_str_t *name, *value, *s;
size_t nargs;
ngx_uint_t n;
value = cf->args->elts;
name = &value[0];
ec = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_enotify_conf_t));
if (ec == NULL) {
return NGX_CONF_ERROR;
}
ec->cmd = value[1];
nargs = cf->args->nelts - 2;
if (nargs) {
if (ngx_array_init(&ec->args, cf->pool, nargs, sizeof(ngx_str_t))
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
s = ngx_array_push_n(&ec->args, nargs);
for (n = 2; n < cf->args->nelts; ++n, ++s) {
*s = value[n];
}
}
enacf = ngx_rtmp_conf_get_module_app_conf(cf, ngx_rtmp_enotify_module);
n = 0;
switch (name->len) {
case sizeof("exec_play") - 1:
n = NGX_RTMP_ENOTIFY_PLAY;
break;
case sizeof("exec_publish") - 1:
n = NGX_RTMP_ENOTIFY_PUBLISH;
break;
case sizeof("exec_play_done") - 1:
n = NGX_RTMP_ENOTIFY_PLAY_DONE;
break;
case sizeof("exec_record_done") - 1:
n = NGX_RTMP_ENOTIFY_RECORD_DONE;
break;
case sizeof("exec_publish_done") - 1:
n = NGX_RTMP_ENOTIFY_PUBLISH_DONE;
break;
}
enacf->event[n] = ec;
return NGX_CONF_OK;
}
static ngx_int_t
ngx_rtmp_enotify_postconfiguration(ngx_conf_t *cf)
{
next_publish = ngx_rtmp_publish;
ngx_rtmp_publish = ngx_rtmp_enotify_publish;
next_play = ngx_rtmp_play;
ngx_rtmp_play = ngx_rtmp_enotify_play;
next_close_stream = ngx_rtmp_close_stream;
ngx_rtmp_close_stream = ngx_rtmp_enotify_close_stream;
next_record_done = ngx_rtmp_record_done;
ngx_rtmp_record_done = ngx_rtmp_enotify_record_done;
return NGX_OK;
}

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -12,17 +13,17 @@
static void
ngx_rtmp_eval_session_str(ngx_rtmp_session_t *s, ngx_rtmp_eval_t *e,
ngx_str_t *ret)
ngx_rtmp_eval_session_str(void *ctx, ngx_rtmp_eval_t *e, ngx_str_t *ret)
{
*ret = *(ngx_str_t *) ((u_char *) s + e->offset);
*ret = *(ngx_str_t *) ((u_char *) ctx + e->offset);
}
static void
ngx_rtmp_eval_connection_str(ngx_rtmp_session_t *s, ngx_rtmp_eval_t *e,
ngx_str_t *ret)
ngx_rtmp_eval_connection_str(void *ctx, ngx_rtmp_eval_t *e, ngx_str_t *ret)
{
ngx_rtmp_session_t *s = ctx;
*ret = *(ngx_str_t *) ((u_char *) s->connection + e->offset);
}
@ -58,15 +59,14 @@ ngx_rtmp_eval_t ngx_rtmp_eval_session[] = {
static void
ngx_rtmp_eval_append(ngx_rtmp_session_t *s, ngx_buf_t *b,
void *data, size_t len)
ngx_rtmp_eval_append(ngx_buf_t *b, void *data, size_t len, ngx_log_t *log)
{
size_t buf_len;
if (b->last + len > b->end) {
buf_len = 2 * (b->last - b->pos) + len;
b->start = ngx_palloc(s->connection->pool, buf_len);
b->start = ngx_alloc(buf_len, log);
if (b->start == NULL) {
return;
}
@ -81,8 +81,8 @@ ngx_rtmp_eval_append(ngx_rtmp_session_t *s, ngx_buf_t *b,
static void
ngx_rtmp_eval_append_var(ngx_rtmp_session_t *s, ngx_buf_t *b,
ngx_rtmp_eval_t **e, ngx_str_t *name)
ngx_rtmp_eval_append_var(void *ctx, ngx_buf_t *b, ngx_rtmp_eval_t **e,
ngx_str_t *name, ngx_log_t *log)
{
ngx_uint_t k;
ngx_str_t v;
@ -91,10 +91,10 @@ ngx_rtmp_eval_append_var(ngx_rtmp_session_t *s, ngx_buf_t *b,
for (; *e; ++e) {
for (k = 0, ee = *e; ee->handler; ++k, ++ee) {
if (ee->name.len == name->len &&
ngx_memcmp(ee->name.data, name->data, name->len) == 0)
ngx_memcmp(ee->name.data, name->data, name->len) == 0)
{
ee->handler(s, ee, &v);
ngx_rtmp_eval_append(s, b, v.data, v.len);
ee->handler(ctx, ee, &v);
ngx_rtmp_eval_append(b, v.data, v.len, log);
}
}
}
@ -102,8 +102,8 @@ ngx_rtmp_eval_append_var(ngx_rtmp_session_t *s, ngx_buf_t *b,
ngx_int_t
ngx_rtmp_eval(ngx_rtmp_session_t *s, ngx_str_t *in, ngx_rtmp_eval_t **e,
ngx_str_t *out)
ngx_rtmp_eval(void *ctx, ngx_str_t *in, ngx_rtmp_eval_t **e, ngx_str_t *out,
ngx_log_t *log)
{
u_char c, *p;
ngx_str_t name;
@ -117,8 +117,7 @@ ngx_rtmp_eval(ngx_rtmp_session_t *s, ngx_str_t *in, ngx_rtmp_eval_t **e,
SNAME
} state = NORMAL;
b.pos = b.last = b.start = ngx_palloc(s->connection->pool,
NGX_RTMP_EVAL_BUFLEN);
b.pos = b.last = b.start = ngx_alloc(NGX_RTMP_EVAL_BUFLEN, log);
if (b.pos == NULL) {
return NGX_ERROR;
}
@ -137,7 +136,7 @@ ngx_rtmp_eval(ngx_rtmp_session_t *s, ngx_str_t *in, ngx_rtmp_eval_t **e,
}
name.len = p - name.data;
ngx_rtmp_eval_append_var(s, &b, e, &name);
ngx_rtmp_eval_append_var(ctx, &b, e, &name, log);
state = NORMAL;
@ -154,7 +153,8 @@ ngx_rtmp_eval(ngx_rtmp_session_t *s, ngx_str_t *in, ngx_rtmp_eval_t **e,
}
name.len = p - name.data;
ngx_rtmp_eval_append_var(s, &b, e, &name);
ngx_rtmp_eval_append_var(ctx, &b, e, &name, log);
/* fall through */
case NORMAL:
switch (c) {
@ -165,10 +165,14 @@ ngx_rtmp_eval(ngx_rtmp_session_t *s, ngx_str_t *in, ngx_rtmp_eval_t **e,
case '\\':
state = ESCAPE;
continue;
/* fall through */
default:
break;
}
/* fall through */
case ESCAPE:
ngx_rtmp_eval_append(s, &b, &c, 1);
ngx_rtmp_eval_append(&b, &c, 1, log);
state = NORMAL;
break;
@ -178,11 +182,11 @@ ngx_rtmp_eval(ngx_rtmp_session_t *s, ngx_str_t *in, ngx_rtmp_eval_t **e,
if (state == NAME) {
p = &in->data[n];
name.len = p - name.data;
ngx_rtmp_eval_append_var(s, &b, e, &name);
ngx_rtmp_eval_append_var(ctx, &b, e, &name, log);
}
c = 0;
ngx_rtmp_eval_append(s, &b, &c, 1);
ngx_rtmp_eval_append(&b, &c, 1, log);
out->data = b.pos;
out->len = b.last - b.pos - 1;
@ -247,7 +251,7 @@ ngx_rtmp_eval_streams(ngx_str_t *in)
}
if (*path == (u_char) '&') {
path++;
v = ngx_atoi(path, in->data + in->len - path);
if (v == NGX_ERROR) {
@ -271,7 +275,7 @@ ngx_rtmp_eval_streams(ngx_str_t *in)
}
dup2(src, dst);
if (close_src) {
ngx_close_file(src);
}

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -15,7 +16,7 @@
typedef struct ngx_rtmp_eval_s ngx_rtmp_eval_t;
typedef void (* ngx_rtmp_eval_pt)(ngx_rtmp_session_t *s, ngx_rtmp_eval_t *e,
typedef void (* ngx_rtmp_eval_pt)(void *ctx, ngx_rtmp_eval_t *e,
ngx_str_t *ret);
@ -33,8 +34,8 @@ struct ngx_rtmp_eval_s {
extern ngx_rtmp_eval_t ngx_rtmp_eval_session[];
ngx_int_t ngx_rtmp_eval(ngx_rtmp_session_t *s, ngx_str_t *in,
ngx_rtmp_eval_t **e, ngx_str_t *out);
ngx_int_t ngx_rtmp_eval(void *ctx, ngx_str_t *in, ngx_rtmp_eval_t **e,
ngx_str_t *out, ngx_log_t *log);
ngx_int_t ngx_rtmp_eval_streams(ngx_str_t *in);

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -12,7 +13,7 @@
static ngx_int_t ngx_rtmp_flv_postconfiguration(ngx_conf_t *cf);
static void ngx_rtmp_flv_read_meta(ngx_rtmp_session_t *s, ngx_file_t *f);
static ngx_int_t ngx_rtmp_flv_timestamp_to_offset(ngx_rtmp_session_t *s,
static ngx_int_t ngx_rtmp_flv_timestamp_to_offset(ngx_rtmp_session_t *s,
ngx_file_t *f, ngx_int_t timestamp);
static ngx_int_t ngx_rtmp_flv_init(ngx_rtmp_session_t *s, ngx_file_t *f,
ngx_int_t aindex, ngx_int_t vindex);
@ -46,7 +47,6 @@ typedef struct {
#define NGX_RTMP_FLV_BUFFER (1024*1024)
#define NGX_RTMP_FLV_DEFAULT_BUFLEN 1000
#define NGX_RTMP_FLV_BUFLEN_ADDON 1000
#define NGX_RTMP_FLV_TAG_HEADER 11
#define NGX_RTMP_FLV_DATA_OFFSET 13
@ -121,29 +121,29 @@ ngx_rtmp_flv_init_index(ngx_rtmp_session_t *s, ngx_chain_t *in)
static ngx_rtmp_amf_elt_t in_keyframes[] = {
{ NGX_RTMP_AMF_ARRAY | NGX_RTMP_AMF_CONTEXT,
{ NGX_RTMP_AMF_ARRAY | NGX_RTMP_AMF_CONTEXT,
ngx_string("filepositions"),
&filepositions_ctx, 0 },
{ NGX_RTMP_AMF_ARRAY | NGX_RTMP_AMF_CONTEXT,
{ NGX_RTMP_AMF_ARRAY | NGX_RTMP_AMF_CONTEXT,
ngx_string("times"),
&times_ctx, 0 }
};
static ngx_rtmp_amf_elt_t in_inf[] = {
{ NGX_RTMP_AMF_OBJECT,
{ NGX_RTMP_AMF_OBJECT,
ngx_string("keyframes"),
in_keyframes, sizeof(in_keyframes) }
};
static ngx_rtmp_amf_elt_t in_elts[] = {
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
NULL, 0 },
{ NGX_RTMP_AMF_OBJECT,
{ NGX_RTMP_AMF_OBJECT,
ngx_null_string,
in_inf, sizeof(in_inf) },
};
@ -160,15 +160,15 @@ ngx_rtmp_flv_init_index(ngx_rtmp_session_t *s, ngx_chain_t *in)
ngx_memzero(&filepositions_ctx, sizeof(filepositions_ctx));
ngx_memzero(&times_ctx, sizeof(times_ctx));
if (ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
if (ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
{
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"flv: init index error");
return NGX_OK;
}
if (filepositions_ctx.link && ngx_rtmp_flv_fill_index(&filepositions_ctx,
if (filepositions_ctx.link && ngx_rtmp_flv_fill_index(&filepositions_ctx,
&ctx->filepositions)
!= NGX_OK)
{
@ -210,7 +210,7 @@ ngx_rtmp_flv_index_value(void *src)
static ngx_int_t
ngx_rtmp_flv_timestamp_to_offset(ngx_rtmp_session_t *s, ngx_file_t *f,
ngx_rtmp_flv_timestamp_to_offset(ngx_rtmp_session_t *s, ngx_file_t *f,
ngx_int_t timestamp)
{
ngx_rtmp_flv_ctx_t *ctx;
@ -225,7 +225,7 @@ ngx_rtmp_flv_timestamp_to_offset(ngx_rtmp_session_t *s, ngx_file_t *f,
}
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"flv: lookup index start timestamp=%i",
"flv: lookup index start timestamp=%i",
timestamp);
if (ctx->meta_read == 0) {
@ -234,7 +234,7 @@ ngx_rtmp_flv_timestamp_to_offset(ngx_rtmp_session_t *s, ngx_file_t *f,
}
if (timestamp <= 0 || ctx->filepositions.nelts == 0
|| ctx->times.nelts == 0)
|| ctx->times.nelts == 0)
{
goto rewind;
}
@ -264,7 +264,7 @@ ngx_rtmp_flv_timestamp_to_offset(ngx_rtmp_session_t *s, ngx_file_t *f,
index * 9 + 1) * 1000;
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"flv: lookup times index=%ui value=%ui",
"flv: lookup times index=%ui value=%ui",
index, (ngx_uint_t) v);
if (timestamp < v) {
@ -274,7 +274,7 @@ ngx_rtmp_flv_timestamp_to_offset(ngx_rtmp_session_t *s, ngx_file_t *f,
if (index >= ctx->filepositions.nelts) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"flv: index out of bounds: %ui>=%ui",
"flv: index out of bounds: %ui>=%ui",
index, ctx->filepositions.nelts);
goto rewind;
}
@ -294,16 +294,16 @@ ngx_rtmp_flv_timestamp_to_offset(ngx_rtmp_session_t *s, ngx_file_t *f,
ret = (ngx_uint_t) ngx_rtmp_flv_index_value(ngx_rtmp_flv_buffer);
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"flv: lookup index timestamp=%i offset=%ui",
"flv: lookup index timestamp=%i offset=%ui",
timestamp, ret);
return ret;
rewind:
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"flv: lookup index timestamp=%i offset=begin",
"flv: lookup index timestamp=%i offset=begin",
timestamp);
return NGX_RTMP_FLV_DATA_OFFSET;
}
@ -329,9 +329,9 @@ ngx_rtmp_flv_read_meta(ngx_rtmp_session_t *s, ngx_file_t *f)
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"flv: read meta");
/* read tag header */
n = ngx_read_file(f, ngx_rtmp_flv_header, sizeof(ngx_rtmp_flv_header),
n = ngx_read_file(f, ngx_rtmp_flv_header, sizeof(ngx_rtmp_flv_header),
NGX_RTMP_FLV_DATA_OFFSET);
if (n != sizeof(ngx_rtmp_flv_header)) {
@ -366,7 +366,7 @@ ngx_rtmp_flv_read_meta(ngx_rtmp_session_t *s, ngx_file_t *f)
/* read metadata */
n = ngx_read_file(f, ngx_rtmp_flv_buffer, size,
sizeof(ngx_rtmp_flv_header) +
sizeof(ngx_rtmp_flv_header) +
NGX_RTMP_FLV_DATA_OFFSET);
if (n != (ssize_t) size) {
@ -425,7 +425,7 @@ ngx_rtmp_flv_send(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_uint_t *ts)
"flv: read tag at offset=%i", ctx->offset);
/* read tag header */
n = ngx_read_file(f, ngx_rtmp_flv_header,
n = ngx_read_file(f, ngx_rtmp_flv_header,
sizeof(ngx_rtmp_flv_header), ctx->offset);
if (n != sizeof(ngx_rtmp_flv_header)) {
@ -471,7 +471,7 @@ ngx_rtmp_flv_send(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_uint_t *ts)
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"flv: read tag type=%i size=%uD timestamp=%uD "
"last_timestamp=%uD",
"last_timestamp=%uD",
(ngx_int_t) h.type,size, h.timestamp, last_timestamp);
lh = h;
@ -479,13 +479,13 @@ ngx_rtmp_flv_send(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_uint_t *ts)
if (size > sizeof(ngx_rtmp_flv_buffer)) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"flv: too big message: %D>%uz", size,
"flv: too big message: %D>%uz", size,
sizeof(ngx_rtmp_flv_buffer));
goto next;
}
/* read tag body */
n = ngx_read_file(f, ngx_rtmp_flv_buffer, size,
n = ngx_read_file(f, ngx_rtmp_flv_buffer, size,
ctx->offset - size - 4);
if (n != (ssize_t) size) {
@ -505,7 +505,7 @@ ngx_rtmp_flv_send(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_uint_t *ts)
/* output chain */
out = ngx_rtmp_append_shared_bufs(cscf, NULL, &in);
ngx_rtmp_prepare_message(s, &h, ctx->msg_mask & (1 << h.type) ?
ngx_rtmp_prepare_message(s, &h, ctx->msg_mask & (1 << h.type) ?
&lh : NULL, out);
rc = ngx_rtmp_send_message(s, out, 0);
ngx_rtmp_free_shared_chain(cscf, out);
@ -530,8 +530,8 @@ next:
return NGX_OK;
}
buflen = (s->buflen ? s->buflen + NGX_RTMP_FLV_BUFLEN_ADDON:
NGX_RTMP_FLV_DEFAULT_BUFLEN);
buflen = s->buflen + NGX_RTMP_FLV_BUFLEN_ADDON;
end_timestamp = (ngx_current_msec - ctx->epoch) +
ctx->start_timestamp + buflen;
@ -541,6 +541,8 @@ next:
h.timestamp > end_timestamp ? h.timestamp - end_timestamp : 0,
h.timestamp, end_timestamp, (ngx_int_t) buflen);
s->current_time = h.timestamp;
/* too much data sent; schedule timeout */
if (h.timestamp > end_timestamp) {
return h.timestamp - end_timestamp;
@ -651,7 +653,7 @@ ngx_rtmp_flv_postconfiguration(ngx_conf_t *cf)
}
fmt = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_play_fmt_t));
if (fmt == NULL) {
return NGX_ERROR;
}

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -24,7 +25,7 @@ ngx_rtmp_bandwidth_t ngx_rtmp_bw_in;
#ifdef NGX_DEBUG
char*
ngx_rtmp_message_type(uint8_t type)
ngx_rtmp_message_type(uint8_t type)
{
static char* types[] = {
"?",
@ -59,7 +60,7 @@ ngx_rtmp_message_type(uint8_t type)
char*
ngx_rtmp_user_message_type(uint16_t evt)
ngx_rtmp_user_message_type(uint16_t evt)
{
static char* evts[] = {
"stream_begin",
@ -98,14 +99,14 @@ ngx_rtmp_cycle(ngx_rtmp_session_t *s)
static ngx_chain_t *
ngx_rtmp_alloc_in_buf(ngx_rtmp_session_t *s)
ngx_rtmp_alloc_in_buf(ngx_rtmp_session_t *s)
{
ngx_chain_t *cl;
ngx_buf_t *b;
size_t size;
if ((cl = ngx_alloc_chain_link(s->in_pool)) == NULL
|| (cl->buf = ngx_calloc_buf(s->in_pool)) == NULL)
|| (cl->buf = ngx_calloc_buf(s->in_pool)) == NULL)
{
return NULL;
}
@ -143,7 +144,7 @@ ngx_rtmp_reset_ping(ngx_rtmp_session_t *s)
}
static void
static void
ngx_rtmp_ping(ngx_event_t *pev)
{
ngx_connection_t *c;
@ -162,14 +163,14 @@ ngx_rtmp_ping(ngx_event_t *pev)
}
if (s->ping_active) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"ping: unresponded");
ngx_rtmp_finalize_session(s);
return;
}
if (cscf->busy) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"ping: not busy between pings");
ngx_rtmp_finalize_session(s);
return;
@ -240,13 +241,15 @@ 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);
}
} else {
} else {
if (old_pos) {
b->pos = b->last = b->start;
@ -271,8 +274,15 @@ ngx_rtmp_recv(ngx_event_t *rev)
b->last += n;
s->in_bytes += n;
if (s->in_bytes >= 0xf0000000) {
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, c->log, 0,
"resetting byte counter");
s->in_bytes = 0;
s->in_last_ack = 0;
}
if (s->ack_size && s->in_bytes - s->in_last_ack >= s->ack_size) {
s->in_last_ack = s->in_bytes;
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0,
@ -347,7 +357,7 @@ ngx_rtmp_recv(ngx_event_t *rev)
if (fmt <= 2 ) {
if (b->last - p < 3)
continue;
/* timestamp:
/* timestamp:
* big-endian 3b -> little-endian 4b */
pp = (u_char*)&timestamp;
pp[2] = *p++;
@ -361,7 +371,7 @@ ngx_rtmp_recv(ngx_event_t *rev)
if (b->last - p < 4)
continue;
/* size:
* big-endian 3b -> little-endian 4b
* big-endian 3b -> little-endian 4b
* type:
* 1b -> 1b*/
pp = (u_char*)&h->mlen;
@ -431,7 +441,7 @@ ngx_rtmp_recv(ngx_event_t *rev)
size = b->last - b->pos;
fsize = h->mlen - st->len;
if (size < ngx_min(fsize, s->in_chunk_size))
if (size < ngx_min(fsize, s->in_chunk_size))
continue;
/* buffer is ready */
@ -494,7 +504,7 @@ ngx_rtmp_send(ngx_event_t *wev)
}
if (wev->timedout) {
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
"client timed out");
c->timedout = 1;
ngx_rtmp_finalize_session(s);
@ -550,12 +560,16 @@ ngx_rtmp_send(ngx_event_t *wev)
ngx_del_event(wev, NGX_WRITE_EVENT, 0);
}
#if (nginx_version >= 1007012)
ngx_event_process_posted((ngx_cycle_t *) ngx_cycle,(ngx_queue_t *) &s->posted_dry_events);
#else
ngx_event_process_posted((ngx_cycle_t *) ngx_cycle, &s->posted_dry_events);
#endif
}
void
ngx_rtmp_prepare_message(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
void
ngx_rtmp_prepare_message(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_rtmp_header_t *lh, ngx_chain_t *out)
{
ngx_chain_t *l;
@ -581,7 +595,7 @@ ngx_rtmp_prepare_message(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
/* detect packet size */
mlen = 0;
nbufs = 0;
nbufs = 0;
for(l = out; l; l = l->next) {
mlen += (l->buf->last - l->buf->pos);
++nbufs;
@ -680,7 +694,7 @@ ngx_rtmp_prepare_message(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
*p++ = pp[1];
*p++ = pp[0];
/* This CONTRADICTS the standard
/* This CONTRADICTS the standard
* but that's the way flash client
* wants data to be encoded;
* ffmpeg complains */
@ -699,7 +713,7 @@ ngx_rtmp_prepare_message(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_int_t
ngx_rtmp_send_message(ngx_rtmp_session_t *s, ngx_chain_t *out,
ngx_rtmp_send_message(ngx_rtmp_session_t *s, ngx_chain_t *out,
ngx_uint_t priority)
{
ngx_uint_t nmsg;
@ -710,7 +724,7 @@ ngx_rtmp_send_message(ngx_rtmp_session_t *s, ngx_chain_t *out,
priority = 3;
}
/* drop packet?
/* drop packet?
* Note we always leave 1 slot free */
if (nmsg + priority * s->out_queue / 4 >= s->out_queue) {
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
@ -741,7 +755,7 @@ ngx_rtmp_send_message(ngx_rtmp_session_t *s, ngx_chain_t *out,
}
ngx_int_t
ngx_int_t
ngx_rtmp_receive_message(ngx_rtmp_session_t *s,
ngx_rtmp_header_t *h, ngx_chain_t *in)
{
@ -757,14 +771,14 @@ ngx_rtmp_receive_message(ngx_rtmp_session_t *s,
int nbufs;
ngx_chain_t *ch;
for(nbufs = 1, ch = in;
ch->next;
for(nbufs = 1, ch = in;
ch->next;
ch = ch->next, ++nbufs);
ngx_log_debug7(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"RTMP recv %s (%d) csid=%D timestamp=%D "
"mlen=%D msid=%D nbufs=%d",
ngx_rtmp_message_type(h->type), (int)h->type,
ngx_rtmp_message_type(h->type), (int)h->type,
h->csid, h->timestamp, h->mlen, h->msid, nbufs);
}
#endif
@ -787,7 +801,7 @@ ngx_rtmp_receive_message(ngx_rtmp_session_t *s,
}
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"calling handler %d", n);
switch ((*evh)(s, h, in)) {
case NGX_ERROR:
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
@ -802,7 +816,7 @@ ngx_rtmp_receive_message(ngx_rtmp_session_t *s,
}
ngx_int_t
ngx_int_t
ngx_rtmp_set_chunk_size(ngx_rtmp_session_t *s, ngx_uint_t size)
{
ngx_rtmp_core_srv_conf_t *cscf;
@ -847,7 +861,7 @@ ngx_rtmp_set_chunk_size(ngx_rtmp_session_t *s, ngx_uint_t size)
bo = lo->buf;
if (bo->end - bo->last >= bi->last - bi->pos) {
bo->last = ngx_cpymem(bo->last, bi->pos,
bo->last = ngx_cpymem(bo->last, bi->pos,
bi->last - bi->pos);
li = li->next;
if (li == fli) {
@ -858,8 +872,9 @@ ngx_rtmp_set_chunk_size(ngx_rtmp_session_t *s, ngx_uint_t size)
continue;
}
bi->pos += (ngx_cpymem(bo->last, bi->pos,
bi->pos += (ngx_cpymem(bo->last, bi->pos,
bo->end - bo->last) - bo->last);
bo->last = bo->end;
lo->next = ngx_rtmp_alloc_in_buf(s);
lo = lo->next;
if (lo == NULL) {
@ -873,13 +888,13 @@ ngx_rtmp_set_chunk_size(ngx_rtmp_session_t *s, ngx_uint_t size)
}
static ngx_int_t
static ngx_int_t
ngx_rtmp_finalize_set_chunk_size(ngx_rtmp_session_t *s)
{
if (s->in_chunk_size_changing && s->in_old_pool) {
ngx_destroy_pool(s->in_old_pool);
s->in_old_pool = NULL;
s->in_chunk_size_changing = 0;
s->in_chunk_size_changing = 0;
}
return NGX_OK;
}

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -33,27 +34,27 @@ static void ngx_rtmp_handshake_done(ngx_rtmp_session_t *s);
/* Handshake keys */
static u_char
static u_char
ngx_rtmp_server_key[] = {
'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ',
'F', 'l', 'a', 's', 'h', ' ', 'M', 'e', 'd', 'i', 'a', ' ',
'S', 'e', 'r', 'v', 'e', 'r', ' ',
'S', 'e', 'r', 'v', 'e', 'r', ' ',
'0', '0', '1',
0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1,
0x02, 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB,
0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1,
0x02, 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB,
0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
};
static u_char
static u_char
ngx_rtmp_client_key[] = {
'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ',
'F', 'l', 'a', 's', 'h', ' ', 'P', 'l', 'a', 'y', 'e', 'r', ' ',
'F', 'l', 'a', 's', 'h', ' ', 'P', 'l', 'a', 'y', 'e', 'r', ' ',
'0', '0', '1',
0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1,
0x02, 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB,
0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1,
0x02, 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB,
0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
};
@ -70,7 +71,7 @@ ngx_rtmp_client_version[4] = {
};
#define NGX_RTMP_HANDSHAKE_KEYLEN SHA256_DIGEST_LENGTH
#define NGX_RTMP_HANDSHAKE_KEYLEN SHA256_DIGEST_LENGTH
#define NGX_RTMP_HANDSHAKE_BUFSIZE 1537
@ -88,7 +89,7 @@ ngx_rtmp_client_version[4] = {
#define NGX_RTMP_HANDSHAKE_CLIENT_DONE 10
static ngx_str_t ngx_rtmp_server_full_key
static ngx_str_t ngx_rtmp_server_full_key
= { sizeof(ngx_rtmp_server_key), ngx_rtmp_server_key };
static ngx_str_t ngx_rtmp_server_partial_key
= { 36, ngx_rtmp_server_key };
@ -100,33 +101,40 @@ static ngx_str_t ngx_rtmp_client_partial_key
static ngx_int_t
ngx_rtmp_make_digest(ngx_str_t *key, ngx_buf_t *src,
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;
}
@ -159,7 +167,7 @@ ngx_rtmp_find_digest(ngx_buf_t *b, ngx_str_t *key, size_t base, ngx_log_t *log)
static ngx_int_t
ngx_rtmp_write_digest(ngx_buf_t *b, ngx_str_t *key, size_t base,
ngx_rtmp_write_digest(ngx_buf_t *b, ngx_str_t *key, size_t base,
ngx_log_t *log)
{
size_t n, offs;
@ -269,7 +277,7 @@ ngx_rtmp_handshake_create_challenge(ngx_rtmp_session_t *s,
static ngx_int_t
ngx_rtmp_handshake_parse_challenge(ngx_rtmp_session_t *s,
ngx_rtmp_handshake_parse_challenge(ngx_rtmp_session_t *s,
ngx_str_t *peer_key, ngx_str_t *key)
{
ngx_buf_t *b;
@ -278,8 +286,8 @@ ngx_rtmp_handshake_parse_challenge(ngx_rtmp_session_t *s,
b = s->hs_buf;
if (*b->pos != '\x03') {
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"handshake: unexpected RTMP version: %i",
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"handshake: unexpected RTMP version: %i",
(ngx_int_t)*b->pos);
return NGX_ERROR;
}
@ -303,7 +311,7 @@ ngx_rtmp_handshake_parse_challenge(ngx_rtmp_session_t *s,
offs = ngx_rtmp_find_digest(b, peer_key, 8, s->connection->log);
}
if (offs == NGX_ERROR) {
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"handshake: digest not found");
s->hs_old = 1;
return NGX_OK;
@ -313,7 +321,7 @@ ngx_rtmp_handshake_parse_challenge(ngx_rtmp_session_t *s,
b->pos += offs;
b->last = b->pos + NGX_RTMP_HANDSHAKE_KEYLEN;
s->hs_digest = ngx_palloc(s->connection->pool, NGX_RTMP_HANDSHAKE_KEYLEN);
if (ngx_rtmp_make_digest(key, b, NULL, s->hs_digest, s->connection->log)
if (ngx_rtmp_make_digest(key, b, NULL, s->hs_digest, s->connection->log)
!= NGX_OK)
{
return NGX_ERROR;
@ -353,8 +361,8 @@ ngx_rtmp_handshake_done(ngx_rtmp_session_t *s)
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"handshake: done");
if (ngx_rtmp_fire_event(s, NGX_RTMP_HANDSHAKE_DONE,
NULL, NULL) != NGX_OK)
if (ngx_rtmp_fire_event(s, NGX_RTMP_HANDSHAKE_DONE,
NULL, NULL) != NGX_OK)
{
ngx_rtmp_finalize_session(s);
return;
@ -380,7 +388,7 @@ ngx_rtmp_handshake_recv(ngx_event_t *rev)
}
if (rev->timedout) {
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
"handshake: recv: client timed out");
c->timedout = 1;
ngx_rtmp_finalize_session(s);
@ -422,25 +430,25 @@ ngx_rtmp_handshake_recv(ngx_event_t *rev)
switch (s->hs_stage) {
case NGX_RTMP_HANDSHAKE_SERVER_SEND_CHALLENGE:
if (ngx_rtmp_handshake_parse_challenge(s,
if (ngx_rtmp_handshake_parse_challenge(s,
&ngx_rtmp_client_partial_key,
&ngx_rtmp_server_full_key) != NGX_OK)
{
ngx_log_error(NGX_LOG_INFO, c->log, 0,
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"handshake: error parsing challenge");
ngx_rtmp_finalize_session(s);
return;
}
if (s->hs_old) {
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"handshake: old-style challenge");
s->hs_buf->pos = s->hs_buf->start;
s->hs_buf->last = s->hs_buf->end;
} else if (ngx_rtmp_handshake_create_challenge(s,
ngx_rtmp_server_version,
&ngx_rtmp_server_partial_key) != NGX_OK)
&ngx_rtmp_server_partial_key) != NGX_OK)
{
ngx_log_error(NGX_LOG_INFO, c->log, 0,
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"handshake: error creating challenge");
ngx_rtmp_finalize_session(s);
return;
@ -453,11 +461,11 @@ ngx_rtmp_handshake_recv(ngx_event_t *rev)
break;
case NGX_RTMP_HANDSHAKE_CLIENT_RECV_RESPONSE:
if (ngx_rtmp_handshake_parse_challenge(s,
if (ngx_rtmp_handshake_parse_challenge(s,
&ngx_rtmp_server_partial_key,
&ngx_rtmp_client_full_key) != NGX_OK)
{
ngx_log_error(NGX_LOG_INFO, c->log, 0,
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"handshake: error parsing challenge");
ngx_rtmp_finalize_session(s);
return;
@ -468,7 +476,7 @@ ngx_rtmp_handshake_recv(ngx_event_t *rev)
case NGX_RTMP_HANDSHAKE_CLIENT_SEND_RESPONSE:
if (ngx_rtmp_handshake_create_response(s) != NGX_OK) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"handshake: response error");
ngx_rtmp_finalize_session(s);
return;
@ -495,7 +503,7 @@ ngx_rtmp_handshake_send(ngx_event_t *wev)
}
if (wev->timedout) {
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
"handshake: send: client timed out");
c->timedout = 1;
ngx_rtmp_finalize_session(s);
@ -538,12 +546,12 @@ ngx_rtmp_handshake_send(ngx_event_t *wev)
switch (s->hs_stage) {
case NGX_RTMP_HANDSHAKE_SERVER_SEND_RESPONSE:
if (s->hs_old) {
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"handshake: old-style response");
s->hs_buf->pos = s->hs_buf->start + 1;
s->hs_buf->last = s->hs_buf->end;
} else if (ngx_rtmp_handshake_create_response(s) != NGX_OK) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"handshake: response error");
ngx_rtmp_finalize_session(s);
return;
@ -602,9 +610,9 @@ ngx_rtmp_client_handshake(ngx_rtmp_session_t *s, unsigned async)
s->hs_buf = ngx_rtmp_alloc_handshake_buffer(s);
s->hs_stage = NGX_RTMP_HANDSHAKE_CLIENT_SEND_CHALLENGE;
if (ngx_rtmp_handshake_create_challenge(s,
if (ngx_rtmp_handshake_create_challenge(s,
ngx_rtmp_client_version,
&ngx_rtmp_client_partial_key) != NGX_OK)
&ngx_rtmp_client_partial_key) != NGX_OK)
{
ngx_rtmp_finalize_session(s);
return;

View file

@ -1,11 +1,13 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include "ngx_rtmp.h"
#include "ngx_rtmp_proxy_protocol.h"
static void ngx_rtmp_close_connection(ngx_connection_t *c);
@ -74,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;
@ -108,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;
@ -129,7 +132,12 @@ ngx_rtmp_init_connection(ngx_connection_t *c)
s->auto_pushed = unix_socket;
ngx_rtmp_handshake(s);
if (addr_conf->proxy_protocol) {
ngx_rtmp_proxy_protocol(s);
} else {
ngx_rtmp_handshake(s);
}
}
@ -140,7 +148,7 @@ ngx_rtmp_init_session(ngx_connection_t *c, ngx_rtmp_addr_conf_t *addr_conf)
ngx_rtmp_core_srv_conf_t *cscf;
ngx_rtmp_error_log_ctx_t *ctx;
s = ngx_pcalloc(c->pool, sizeof(ngx_rtmp_session_t) +
s = ngx_pcalloc(c->pool, sizeof(ngx_rtmp_session_t) +
sizeof(ngx_chain_t *) * ((ngx_rtmp_core_srv_conf_t *)
addr_conf->ctx-> srv_conf[ngx_rtmp_core_module
.ctx_index])->out_queue);
@ -183,15 +191,20 @@ ngx_rtmp_init_session(ngx_connection_t *c, ngx_rtmp_addr_conf_t *addr_conf)
s->out_queue = cscf->out_queue;
s->out_cork = cscf->out_cork;
s->in_streams = ngx_pcalloc(c->pool, sizeof(ngx_rtmp_stream_t)
s->in_streams = ngx_pcalloc(c->pool, sizeof(ngx_rtmp_stream_t)
* cscf->max_streams);
if (s->in_streams == NULL) {
ngx_rtmp_close_connection(c);
return NULL;
}
#if (nginx_version >= 1007005)
ngx_queue_init(&s->posted_dry_events);
#endif
s->epoch = ngx_current_msec;
s->timeout = cscf->timeout;
s->buflen = cscf->buflen;
ngx_rtmp_set_chunk_size(s, NGX_RTMP_DEFAULT_CHUNK_SIZE);

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2013 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -79,7 +80,7 @@ ngx_rtmp_limit_create_main_conf(ngx_conf_t *cf)
static ngx_int_t
ngx_rtmp_limit_connect(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_rtmp_limit_connect(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
ngx_rtmp_limit_main_conf_t *lmcf;
@ -117,7 +118,7 @@ ngx_rtmp_limit_connect(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
static ngx_int_t
ngx_rtmp_limit_disconnect(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_rtmp_limit_disconnect(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
ngx_rtmp_limit_main_conf_t *lmcf;
@ -183,7 +184,7 @@ ngx_rtmp_limit_postconfiguration(ngx_conf_t *cf)
h = ngx_array_push(&cmcf->events[NGX_RTMP_CONNECT]);
*h = ngx_rtmp_limit_connect;
h = ngx_array_push(&cmcf->events[NGX_RTMP_DISCONNECT]);
*h = ngx_rtmp_limit_disconnect;

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -20,7 +21,7 @@ static ngx_rtmp_stream_eof_pt next_stream_eof;
static ngx_int_t ngx_rtmp_live_postconfiguration(ngx_conf_t *cf);
static void * ngx_rtmp_live_create_app_conf(ngx_conf_t *cf);
static char * ngx_rtmp_live_merge_app_conf(ngx_conf_t *cf,
static char * ngx_rtmp_live_merge_app_conf(ngx_conf_t *cf,
void *parent, void *child);
static char *ngx_rtmp_live_set_msec_slot(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
@ -37,25 +38,18 @@ static ngx_command_t ngx_rtmp_live_commands[] = {
offsetof(ngx_rtmp_live_app_conf_t, live),
NULL },
{ ngx_string("meta"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
ngx_conf_set_flag_slot,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_rtmp_live_app_conf_t, meta),
NULL },
{ 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"),
@ -100,6 +94,13 @@ static ngx_command_t ngx_rtmp_live_commands[] = {
offsetof(ngx_rtmp_live_app_conf_t, play_restart),
NULL },
{ ngx_string("idle_streams"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
ngx_conf_set_flag_slot,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_rtmp_live_app_conf_t, idle_streams),
NULL },
{ ngx_string("drop_idle_publisher"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
ngx_rtmp_live_set_msec_slot,
@ -150,9 +151,8 @@ ngx_rtmp_live_create_app_conf(ngx_conf_t *cf)
}
lacf->live = NGX_CONF_UNSET;
lacf->meta = 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;
@ -160,6 +160,7 @@ ngx_rtmp_live_create_app_conf(ngx_conf_t *cf)
lacf->wait_video = NGX_CONF_UNSET;
lacf->publish_notify = NGX_CONF_UNSET;
lacf->play_restart = NGX_CONF_UNSET;
lacf->idle_streams = NGX_CONF_UNSET;
return lacf;
}
@ -172,23 +173,23 @@ ngx_rtmp_live_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_rtmp_live_app_conf_t *conf = child;
ngx_conf_merge_value(conf->live, prev->live, 0);
ngx_conf_merge_value(conf->meta, prev->meta, 1);
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);
ngx_conf_merge_value(conf->wait_key, prev->wait_key, 1);
ngx_conf_merge_value(conf->wait_key, prev->wait_key, 0);
ngx_conf_merge_value(conf->wait_video, prev->wait_video, 0);
ngx_conf_merge_value(conf->publish_notify, prev->publish_notify, 0);
ngx_conf_merge_value(conf->play_restart, prev->play_restart, 0);
ngx_conf_merge_value(conf->idle_streams, prev->idle_streams, 1);
conf->pool = ngx_create_pool(4096, &cf->cycle->new_log);
if (conf->pool == NULL) {
return NGX_CONF_ERROR;
}
conf->streams = ngx_pcalloc(cf->pool,
conf->streams = ngx_pcalloc(cf->pool,
sizeof(ngx_rtmp_live_stream_t *) * conf->nbuckets);
return NGX_CONF_OK;
@ -252,7 +253,7 @@ ngx_rtmp_live_get_stream(ngx_rtmp_session_t *s, u_char *name, int create)
*stream = ngx_palloc(lacf->pool, sizeof(ngx_rtmp_live_stream_t));
}
ngx_memzero(*stream, sizeof(ngx_rtmp_live_stream_t));
ngx_memcpy((*stream)->name, name,
ngx_memcpy((*stream)->name, name,
ngx_min(sizeof((*stream)->name) - 1, len));
(*stream)->epoch = ngx_current_msec;
@ -260,7 +261,7 @@ ngx_rtmp_live_get_stream(ngx_rtmp_session_t *s, u_char *name, int create)
}
static void
static void
ngx_rtmp_live_idle(ngx_event_t *pev)
{
ngx_connection_t *c;
@ -356,6 +357,9 @@ ngx_rtmp_live_set_status(ngx_rtmp_session_t *s, ngx_chain_t *control,
ctx->cs[1].active = 0;
ctx->cs[1].dropped = 0;
ctx->cs[2].active = 0;
ctx->cs[2].dropped = 0;
}
@ -510,11 +514,22 @@ ngx_rtmp_live_join(ngx_rtmp_session_t *s, u_char *name, unsigned publisher)
ctx->session = s;
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"live: join '%s'", name);
stream = ngx_rtmp_live_get_stream(s, name, 1);
if (stream == NULL) {
stream = ngx_rtmp_live_get_stream(s, name, publisher || lacf->idle_streams);
if (stream == NULL ||
!(publisher || (*stream)->publishing || lacf->idle_streams))
{
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"live: stream not found");
ngx_rtmp_send_status(s, "NetStream.Play.StreamNotFound", "error",
"No such stream");
ngx_rtmp_finalize_session(s);
return;
}
@ -538,12 +553,13 @@ 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;
}
ctx->cs[0].csid = NGX_RTMP_CSID_VIDEO;
ctx->cs[1].csid = NGX_RTMP_CSID_AUDIO;
ctx->cs[2].csid = NGX_RTMP_CSID_AMF;
if (!ctx->publishing && ctx->stream->active) {
ngx_rtmp_live_start(s);
@ -554,7 +570,8 @@ ngx_rtmp_live_join(ngx_rtmp_session_t *s, u_char *name, unsigned publisher)
static ngx_int_t
ngx_rtmp_live_close_stream(ngx_rtmp_session_t *s, ngx_rtmp_close_stream_t *v)
{
ngx_rtmp_live_ctx_t *ctx, **cctx;
ngx_rtmp_session_t *ss;
ngx_rtmp_live_ctx_t *ctx, **cctx, *pctx;
ngx_rtmp_live_stream_t **stream;
ngx_rtmp_live_app_conf_t *lacf;
@ -595,6 +612,16 @@ ngx_rtmp_live_close_stream(ngx_rtmp_session_t *s, ngx_rtmp_close_stream_t *v)
if (ctx->publishing) {
ngx_rtmp_send_status(s, "NetStream.Unpublish.Success",
"status", "Stop publishing");
if (!lacf->idle_streams) {
for (pctx = ctx->stream->ctx; pctx; pctx = pctx->next) {
if (pctx->publishing == 0) {
ss = pctx->session;
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, ss->connection->log, 0,
"live: no publisher");
ngx_rtmp_finalize_session(ss);
}
}
}
}
if (ctx->stream->ctx) {
@ -670,7 +697,7 @@ next:
}
static ngx_int_t
ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
ngx_rtmp_live_ctx_t *ctx, *pctx;
@ -726,6 +753,8 @@ ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
"live: %s packet timestamp=%uD",
type_s, h->timestamp);
s->current_time = h->timestamp;
peers = 0;
apkt = NULL;
aapkt = NULL;
@ -758,7 +787,7 @@ ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
lh.timestamp = cs->timestamp;
}
clh = ch;
clh = lh;
clh.type = (h->type == NGX_RTMP_MSG_AUDIO ? NGX_RTMP_MSG_VIDEO :
NGX_RTMP_MSG_AUDIO);
@ -814,7 +843,7 @@ ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
}
}
if (lacf->meta && codec_ctx->meta) {
if (codec_ctx->meta) {
meta = codec_ctx->meta;
meta_version = codec_ctx->meta_version;
}
@ -925,6 +954,7 @@ ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
cs->timestamp = lh.timestamp;
cs->active = 1;
ss->current_time = cs->timestamp;
} else {
@ -946,6 +976,7 @@ ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
cs->timestamp = ch.timestamp;
cs->active = 1;
ss->current_time = cs->timestamp;
++peers;
@ -979,6 +1010,7 @@ ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
cs->timestamp += delta;
++peers;
ss->current_time = cs->timestamp;
}
if (rpkt) {
@ -1000,9 +1032,359 @@ ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_rtmp_update_bandwidth(&ctx->stream->bw_in, h->mlen);
ngx_rtmp_update_bandwidth(&ctx->stream->bw_out, h->mlen * peers);
ngx_rtmp_update_bandwidth(h->type == NGX_RTMP_MSG_AUDIO ?
&ctx->stream->bw_in_audio :
&ctx->stream->bw_in_video,
h->mlen);
return NGX_OK;
}
static ngx_int_t
ngx_rtmp_live_data(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in, ngx_rtmp_amf_elt_t *out_elts, ngx_uint_t out_elts_size)
{
ngx_rtmp_live_ctx_t *ctx, *pctx;
ngx_chain_t *data, *rpkt;
ngx_rtmp_core_srv_conf_t *cscf;
ngx_rtmp_live_app_conf_t *lacf;
ngx_rtmp_session_t *ss;
ngx_rtmp_header_t ch;
ngx_int_t rc;
ngx_uint_t prio;
ngx_uint_t peers;
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) {
return NGX_ERROR;
}
if (!lacf->live || in == NULL || in->buf == NULL) {
return NGX_OK;
}
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_live_module);
if (ctx == NULL || ctx->stream == NULL) {
return NGX_OK;
}
if (ctx->publishing == 0) {
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"live: %s from non-publisher", msg_type);
return NGX_OK;
}
/* drop the data packet if the stream is not active */
if (!ctx->stream->active) {
return NGX_OK;
}
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"live: %s packet timestamp=%uD",
msg_type, h->timestamp);
cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
cs = &ctx->cs[2];
cs->active = 1;
peers = 0;
prio = 0;
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);
}
return NGX_ERROR;
}
ngx_memzero(&ch, sizeof(ch));
ch.timestamp = h->timestamp;
ch.msid = NGX_RTMP_MSID;
ch.csid = h->csid;
ch.type = NGX_RTMP_MSG_AMF_META;
delta = ch.timestamp - cs->timestamp;
rpkt = ngx_rtmp_append_shared_bufs(cscf, data, in);
ngx_rtmp_prepare_message(s, &ch, NULL, rpkt);
for (pctx = ctx->stream->ctx; pctx; pctx = pctx->next) {
if (pctx == ctx || pctx->paused) {
continue;
}
ss = pctx->session;
if (ngx_rtmp_send_message(ss, rpkt, prio) != NGX_OK) {
++pctx->ndropped;
cs->dropped += delta;
continue;
}
cs->timestamp += delta;
++peers;
ss->current_time = cs->timestamp;
}
if (rpkt) {
ngx_rtmp_free_shared_chain(cscf, rpkt);
}
ngx_rtmp_update_bandwidth(&ctx->stream->bw_in, h->mlen);
ngx_rtmp_update_bandwidth(&ctx->stream->bw_out, h->mlen * peers);
ngx_rtmp_update_bandwidth(&ctx->stream->bw_in_data, h->mlen);
return NGX_OK;
}
static ngx_int_t
ngx_rtmp_live_on_cue_point(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
static ngx_rtmp_amf_elt_t out_elts[] = {
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
"onCuePoint", 0 }
};
return ngx_rtmp_live_data(s, h, in, out_elts,
sizeof(out_elts) / sizeof(out_elts[0]));
}
static ngx_int_t
ngx_rtmp_live_on_text_data(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
static ngx_rtmp_amf_elt_t out_elts[] = {
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
"onTextData", 0 }
};
return ngx_rtmp_live_data(s, h, in, out_elts,
sizeof(out_elts) / sizeof(out_elts[0]));
}
static ngx_int_t
ngx_rtmp_live_on_fi(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
ngx_rtmp_live_app_conf_t *lacf;
ngx_int_t res;
static struct {
u_char time[NGX_TIME_T_LEN + 1];
u_char date[NGX_TIME_T_LEN + 1];
} v;
static ngx_rtmp_amf_elt_t in_dt_elts[] = {
{ NGX_RTMP_AMF_STRING,
ngx_string("sd"),
&v.date, sizeof(v.date) },
{ NGX_RTMP_AMF_STRING,
ngx_string("st"),
&v.time, sizeof(v.time) },
};
static ngx_rtmp_amf_elt_t in_elts[] = {
{ NGX_RTMP_AMF_MIXED_ARRAY,
ngx_null_string,
in_dt_elts, sizeof(in_dt_elts) },
};
static ngx_rtmp_amf_elt_t out_dt_elts[] = {
{ NGX_RTMP_AMF_STRING,
ngx_string("sd"),
NULL, 0 },
{ NGX_RTMP_AMF_STRING,
ngx_string("st"),
NULL, 0 },
};
static ngx_rtmp_amf_elt_t out_elts[] = {
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
"onFI", 0 },
{ NGX_RTMP_AMF_MIXED_ARRAY,
ngx_null_string,
out_dt_elts, sizeof(out_dt_elts) },
};
lacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_live_module);
if (lacf == NULL) {
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"live: Fi - no live config!");
return NGX_ERROR;
}
if (!lacf->live || in == NULL || in->buf == NULL) {
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"live: Fi - no live or no buffer!");
return NGX_OK;
}
ngx_memzero(&v, sizeof(v));
res = ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0]));
if (res == NGX_OK) {
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"live: onFI: date='%s', time='%s'",
v.date, v.time);
out_dt_elts[0].data = v.date;
out_dt_elts[1].data = v.time;
// Pass through datetime from publisher
return ngx_rtmp_live_data(s, h, in, out_elts,
sizeof(out_elts) / sizeof(out_elts[0]));
} else {
// Send our server datetime
return ngx_rtmp_send_fi(s);
}
}
static ngx_int_t
ngx_rtmp_live_on_fcpublish(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
ngx_rtmp_live_app_conf_t *lacf;
static struct {
double trans;
u_char action[128];
u_char stream[1024];
} v;
static ngx_rtmp_amf_elt_t in_elts[] = {
// Already readed
/* { NGX_RTMP_AMF_STRING,
ngx_null_string,
&v.action, sizeof(v.action) },
*/
{ NGX_RTMP_AMF_NUMBER,
ngx_null_string,
&v.trans, 0 },
{ NGX_RTMP_AMF_NULL,
ngx_null_string,
NULL, 0 },
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
&v.stream, sizeof(v.stream) },
};
lacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_live_module);
if (lacf == NULL) {
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"live: FCPublish - no live config!");
return NGX_ERROR;
}
if (!lacf->live || in == NULL || in->buf == NULL) {
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"live: FCPublish - no live or no buffer!");
return NGX_OK;
}
ngx_memzero(&v, sizeof(v));
ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0]));
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"live: onFCPublish: stream='%s'",
v.stream);
return ngx_rtmp_send_fcpublish(s, v.stream);
}
static ngx_int_t
ngx_rtmp_live_on_fcunpublish(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
ngx_rtmp_live_app_conf_t *lacf;
static struct {
double trans;
u_char action[128];
u_char stream[1024];
} v;
static ngx_rtmp_amf_elt_t in_elts[] = {
// Already readed
/* { NGX_RTMP_AMF_STRING,
ngx_null_string,
&v.action, sizeof(v.action) },
*/
{ NGX_RTMP_AMF_NUMBER,
ngx_null_string,
&v.trans, 0 },
{ NGX_RTMP_AMF_NULL,
ngx_null_string,
NULL, 0 },
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
&v.stream, sizeof(v.stream) },
};
lacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_live_module);
if (lacf == NULL) {
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"live: FCUnpublish - no live config!");
return NGX_ERROR;
}
if (!lacf->live || in == NULL || in->buf == NULL) {
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"live: FCUnpublish - no live or no buffer!");
return NGX_OK;
}
ngx_memzero(&v, sizeof(v));
ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0]));
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"live: onFCUnpublish: stream='%s'",
v.stream);
return ngx_rtmp_send_fcunpublish(s, v.stream);
}
static ngx_int_t
ngx_rtmp_live_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
@ -1044,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;
@ -1055,7 +1440,7 @@ ngx_rtmp_live_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"live: play: name='%s' start=%uD duration=%uD reset=%d",
v->name, (uint32_t) v->start,
v->name, (uint32_t) v->start,
(uint32_t) v->duration, (uint32_t) v->reset);
/* join stream as subscriber */
@ -1076,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);
}
@ -1085,6 +1472,7 @@ ngx_rtmp_live_postconfiguration(ngx_conf_t *cf)
{
ngx_rtmp_core_main_conf_t *cmcf;
ngx_rtmp_handler_pt *h;
ngx_rtmp_amf_handler_t *ch;
cmcf = ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_core_module);
@ -1116,5 +1504,25 @@ ngx_rtmp_live_postconfiguration(ngx_conf_t *cf)
next_stream_eof = ngx_rtmp_stream_eof;
ngx_rtmp_stream_eof = ngx_rtmp_live_stream_eof;
ch = ngx_array_push(&cmcf->amf);
ngx_str_set(&ch->name, "onTextData");
ch->handler = ngx_rtmp_live_on_text_data;
ch = ngx_array_push(&cmcf->amf);
ngx_str_set(&ch->name, "onCuePoint");
ch->handler = ngx_rtmp_live_on_cue_point;
ch = ngx_array_push(&cmcf->amf);
ngx_str_set(&ch->name, "onFI");
ch->handler = ngx_rtmp_live_on_fi;
ch = ngx_array_push(&cmcf->amf);
ngx_str_set(&ch->name, "FCPublish");
ch->handler = ngx_rtmp_live_on_fcpublish;
ch = ngx_array_push(&cmcf->amf);
ngx_str_set(&ch->name, "FCUnpublish");
ch->handler = ngx_rtmp_live_on_fcunpublish;
return NGX_OK;
}

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -32,7 +33,7 @@ struct ngx_rtmp_live_ctx_s {
ngx_rtmp_live_stream_t *stream;
ngx_rtmp_live_ctx_t *next;
ngx_uint_t ndropped;
ngx_rtmp_live_chunk_stream_t cs[2];
ngx_rtmp_live_chunk_stream_t cs[3];
ngx_uint_t meta_version;
ngx_event_t idle_evt;
unsigned active:1;
@ -47,6 +48,9 @@ struct ngx_rtmp_live_stream_s {
ngx_rtmp_live_stream_t *next;
ngx_rtmp_live_ctx_t *ctx;
ngx_rtmp_bandwidth_t bw_in;
ngx_rtmp_bandwidth_t bw_in_audio;
ngx_rtmp_bandwidth_t bw_in_video;
ngx_rtmp_bandwidth_t bw_in_data;
ngx_rtmp_bandwidth_t bw_out;
ngx_msec_t epoch;
unsigned active:1;
@ -67,7 +71,8 @@ typedef struct {
ngx_flag_t wait_video;
ngx_flag_t publish_notify;
ngx_flag_t play_restart;
ngx_msec_t buflen;
ngx_flag_t idle_streams;
ngx_flag_t buffer;
ngx_pool_t *pool;
ngx_rtmp_live_stream_t *free_streams;
} ngx_rtmp_live_app_conf_t;

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2013 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -15,7 +16,7 @@ static ngx_rtmp_play_pt next_play;
static ngx_int_t ngx_rtmp_log_postconfiguration(ngx_conf_t *cf);
static void *ngx_rtmp_log_create_main_conf(ngx_conf_t *cf);
static void * ngx_rtmp_log_create_app_conf(ngx_conf_t *cf);
static char * ngx_rtmp_log_merge_app_conf(ngx_conf_t *cf,
static char * ngx_rtmp_log_merge_app_conf(ngx_conf_t *cf,
void *parent, void *child);
static char * ngx_rtmp_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
@ -188,6 +189,26 @@ ngx_rtmp_log_var_remote_addr_getdata(ngx_rtmp_session_t *s, u_char *buf,
}
static size_t
ngx_rtmp_log_var_msec_getlen(ngx_rtmp_session_t *s,
ngx_rtmp_log_op_t *op)
{
return NGX_TIME_T_LEN + 4;
}
static u_char *
ngx_rtmp_log_var_msec_getdata(ngx_rtmp_session_t *s, u_char *buf,
ngx_rtmp_log_op_t *op)
{
ngx_time_t *tp;
tp = ngx_timeofday();
return ngx_sprintf(buf, "%T.%03M", tp->sec, tp->msec);
}
static size_t
ngx_rtmp_log_var_session_string_getlen(ngx_rtmp_session_t *s,
ngx_rtmp_log_op_t *op)
@ -430,6 +451,11 @@ static ngx_rtmp_log_var_t ngx_rtmp_log_vars[] = {
ngx_rtmp_log_var_time_local_getdata,
0 },
{ ngx_string("msec"),
ngx_rtmp_log_var_msec_getlen,
ngx_rtmp_log_var_msec_getdata,
0 },
{ ngx_string("session_time"),
ngx_rtmp_log_var_session_time_getlen,
ngx_rtmp_log_var_session_time_getdata,
@ -827,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) {
@ -841,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);
}
@ -888,13 +919,13 @@ ngx_rtmp_log_write(ngx_rtmp_session_t *s, ngx_rtmp_log_t *log, u_char *buf,
static ngx_int_t
ngx_rtmp_log_disconnect(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_rtmp_log_disconnect(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
ngx_rtmp_log_app_conf_t *lacf;
ngx_rtmp_log_t *log;
ngx_rtmp_log_op_t *op;
ngx_uint_t n;
ngx_uint_t n, i;
u_char *line, *p;
size_t len;
@ -908,7 +939,7 @@ ngx_rtmp_log_disconnect(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
}
log = lacf->logs->elts;
for (n = 0; n < lacf->logs->nelts; ++n, ++log) {
for (i = 0; i < lacf->logs->nelts; ++i, ++log) {
if (ngx_time() == log->disk_full_time) {
/* FreeBSD full disk protection;

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -23,6 +24,9 @@ static ngx_int_t ngx_rtmp_mp4_send(ngx_rtmp_session_t *s, ngx_file_t *f,
static ngx_int_t ngx_rtmp_mp4_reset(ngx_rtmp_session_t *s);
#define NGX_RTMP_MP4_MAX_FRAMES 8
#pragma pack(push,4)
@ -216,7 +220,6 @@ ngx_rtmp_mp4_from_rtmp_timestamp(ngx_rtmp_mp4_track_t *t, uint32_t ts)
}
#define NGX_RTMP_MP4_DEFAULT_BUFLEN 1000
#define NGX_RTMP_MP4_BUFLEN_ADDON 1000
@ -246,8 +249,8 @@ ngx_rtmp_mp4_mmap(ngx_fd_t fd, size_t size, off_t offset, ngx_fd_t *extra)
CloseHandle(*extra);
}
/*
* non-NULL result means map view handle is open
/*
* non-NULL result means map view handle is open
* and should be closed later
*/
@ -323,21 +326,21 @@ static ngx_int_t ngx_rtmp_mp4_parse_stco(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_co64(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_avc1(ngx_rtmp_session_t *s, u_char *pos,
static ngx_int_t ngx_rtmp_mp4_parse_avc1(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_avcC(ngx_rtmp_session_t *s, u_char *pos,
static ngx_int_t ngx_rtmp_mp4_parse_avcC(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_mp4a(ngx_rtmp_session_t *s, u_char *pos,
static ngx_int_t ngx_rtmp_mp4_parse_mp4a(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_mp4v(ngx_rtmp_session_t *s, u_char *pos,
static ngx_int_t ngx_rtmp_mp4_parse_mp4v(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_esds(ngx_rtmp_session_t *s, u_char *pos,
static ngx_int_t ngx_rtmp_mp4_parse_esds(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_mp3(ngx_rtmp_session_t *s, u_char *pos,
static ngx_int_t ngx_rtmp_mp4_parse_mp3(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_nmos(ngx_rtmp_session_t *s, u_char *pos,
static ngx_int_t ngx_rtmp_mp4_parse_nmos(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_spex(ngx_rtmp_session_t *s, u_char *pos,
static ngx_int_t ngx_rtmp_mp4_parse_spex(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
@ -356,7 +359,7 @@ static ngx_rtmp_mp4_box_t ngx_rtmp_mp4_boxes[] = {
{ ngx_rtmp_mp4_make_tag('m','d','h','d'), ngx_rtmp_mp4_parse_mdhd },
{ ngx_rtmp_mp4_make_tag('h','d','l','r'), ngx_rtmp_mp4_parse_hdlr },
{ ngx_rtmp_mp4_make_tag('m','i','n','f'), ngx_rtmp_mp4_parse },
{ ngx_rtmp_mp4_make_tag('s','t','b','l'), ngx_rtmp_mp4_parse },
{ ngx_rtmp_mp4_make_tag('s','t','b','l'), ngx_rtmp_mp4_parse },
{ ngx_rtmp_mp4_make_tag('s','t','s','d'), ngx_rtmp_mp4_parse_stsd },
{ ngx_rtmp_mp4_make_tag('s','t','s','c'), ngx_rtmp_mp4_parse_stsc },
{ ngx_rtmp_mp4_make_tag('s','t','t','s'), ngx_rtmp_mp4_parse_stts },
@ -366,28 +369,29 @@ static ngx_rtmp_mp4_box_t ngx_rtmp_mp4_boxes[] = {
{ ngx_rtmp_mp4_make_tag('s','t','z','2'), ngx_rtmp_mp4_parse_stz2 },
{ ngx_rtmp_mp4_make_tag('s','t','c','o'), ngx_rtmp_mp4_parse_stco },
{ ngx_rtmp_mp4_make_tag('c','o','6','4'), ngx_rtmp_mp4_parse_co64 },
{ ngx_rtmp_mp4_make_tag('a','v','c','1'), ngx_rtmp_mp4_parse_avc1 },
{ ngx_rtmp_mp4_make_tag('a','v','c','C'), ngx_rtmp_mp4_parse_avcC },
{ ngx_rtmp_mp4_make_tag('m','p','4','a'), ngx_rtmp_mp4_parse_mp4a },
{ ngx_rtmp_mp4_make_tag('m','p','4','v'), ngx_rtmp_mp4_parse_mp4v },
{ ngx_rtmp_mp4_make_tag('e','s','d','s'), ngx_rtmp_mp4_parse_esds },
{ ngx_rtmp_mp4_make_tag('.','m','p','3'), ngx_rtmp_mp4_parse_mp3 },
{ ngx_rtmp_mp4_make_tag('n','m','o','s'), ngx_rtmp_mp4_parse_nmos },
{ ngx_rtmp_mp4_make_tag('s','p','e','x'), ngx_rtmp_mp4_parse_spex }
{ ngx_rtmp_mp4_make_tag('a','v','c','1'), ngx_rtmp_mp4_parse_avc1 },
{ ngx_rtmp_mp4_make_tag('a','v','c','C'), ngx_rtmp_mp4_parse_avcC },
{ ngx_rtmp_mp4_make_tag('m','p','4','a'), ngx_rtmp_mp4_parse_mp4a },
{ ngx_rtmp_mp4_make_tag('m','p','4','v'), ngx_rtmp_mp4_parse_mp4v },
{ ngx_rtmp_mp4_make_tag('e','s','d','s'), ngx_rtmp_mp4_parse_esds },
{ ngx_rtmp_mp4_make_tag('.','m','p','3'), ngx_rtmp_mp4_parse_mp3 },
{ ngx_rtmp_mp4_make_tag('n','m','o','s'), ngx_rtmp_mp4_parse_nmos },
{ ngx_rtmp_mp4_make_tag('s','p','e','x'), ngx_rtmp_mp4_parse_spex },
{ ngx_rtmp_mp4_make_tag('w','a','v','e'), ngx_rtmp_mp4_parse }
};
static ngx_int_t ngx_rtmp_mp4_parse_descr(ngx_rtmp_session_t *s, u_char *pos,
static ngx_int_t ngx_rtmp_mp4_parse_descr(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_es(ngx_rtmp_session_t *s, u_char *pos,
static ngx_int_t ngx_rtmp_mp4_parse_es(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_dc(ngx_rtmp_session_t *s, u_char *pos,
static ngx_int_t ngx_rtmp_mp4_parse_dc(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_ds(ngx_rtmp_session_t *s, u_char *pos,
static ngx_int_t ngx_rtmp_mp4_parse_ds(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
typedef ngx_int_t (*ngx_rtmp_mp4_descriptor_pt)(ngx_rtmp_session_t *s,
typedef ngx_int_t (*ngx_rtmp_mp4_descriptor_pt)(ngx_rtmp_session_t *s,
u_char *pos, u_char *last);
typedef struct {
@ -458,7 +462,7 @@ ngx_rtmp_mp4_parse_trak(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
}
if (ctx->track && ctx->track->type &&
(ctx->ntracks == 0 ||
(ctx->ntracks == 0 ||
ctx->tracks[0].type != ctx->tracks[ctx->ntracks].type))
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
@ -593,7 +597,7 @@ ngx_rtmp_mp4_parse_hdlr(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
static ngx_int_t
ngx_rtmp_mp4_parse_video(ngx_rtmp_session_t *s, u_char *pos, u_char *last,
ngx_rtmp_mp4_parse_video(ngx_rtmp_session_t *s, u_char *pos, u_char *last,
ngx_int_t codec)
{
ngx_rtmp_mp4_ctx_t *ctx;
@ -627,7 +631,7 @@ ngx_rtmp_mp4_parse_video(ngx_rtmp_session_t *s, u_char *pos, u_char *last,
if (ngx_rtmp_mp4_parse(s, pos, last) != NGX_OK) {
return NGX_ERROR;
}
ctx->track->fhdr = (u_char) ctx->track->codec;
return NGX_OK;
@ -640,6 +644,7 @@ ngx_rtmp_mp4_parse_audio(ngx_rtmp_session_t *s, u_char *pos, u_char *last,
{
ngx_rtmp_mp4_ctx_t *ctx;
u_char *p;
ngx_uint_t version;
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_mp4_module);
@ -653,7 +658,11 @@ ngx_rtmp_mp4_parse_audio(ngx_rtmp_session_t *s, u_char *pos, u_char *last,
return NGX_ERROR;
}
pos += 16;
pos += 8;
version = ngx_rtmp_r16(*(uint16_t *) pos);
pos += 8;
ctx->nchannels = ngx_rtmp_r16(*(uint16_t *) pos);
@ -696,10 +705,24 @@ ngx_rtmp_mp4_parse_audio(ngx_rtmp_session_t *s, u_char *pos, u_char *last,
break;
}
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: audio settings codec=%i, nchannels==%ui, "
ngx_log_debug5(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: audio settings version=%ui, codec=%i, nchannels==%ui, "
"sample_size=%ui, sample_rate=%ui",
codec, ctx->nchannels, ctx->sample_size, ctx->sample_rate);
version, codec, ctx->nchannels, ctx->sample_size,
ctx->sample_rate);
switch (version) {
case 1:
pos += 16;
break;
case 2:
pos += 36;
}
if (pos > last) {
return NGX_ERROR;
}
if (ngx_rtmp_mp4_parse(s, pos, last) != NGX_OK) {
return NGX_ERROR;
@ -822,7 +845,7 @@ ngx_rtmp_mp4_parse_dc(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
}
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: decoder descriptor id=%i codec=%i",
"mp4: decoder descriptor id=%i codec=%i",
(ngx_int_t) id, *pc);
return ngx_rtmp_mp4_parse_descr(s, pos, last);
@ -879,7 +902,7 @@ ngx_rtmp_mp4_parse_descr(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
ngx_uint_t n, ndesc;
ngx_rtmp_mp4_descriptor_t *ds;
ndesc = sizeof(ngx_rtmp_mp4_descriptors)
ndesc = sizeof(ngx_rtmp_mp4_descriptors)
/ sizeof(ngx_rtmp_mp4_descriptors[0]);
while (pos < last) {
@ -916,7 +939,7 @@ ngx_rtmp_mp4_parse_descr(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
}
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: descriptor%s tag=%i size=%uD",
"mp4: descriptor%s tag=%i size=%uD",
ds ? "" : " unhandled", (ngx_int_t) tag, size);
if (ds && ds->handler(s, pos, pos + size) != NGX_OK) {
@ -995,12 +1018,12 @@ ngx_rtmp_mp4_parse_stsc(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
t->chunks = (ngx_rtmp_mp4_chunks_t *) pos;
if (pos + sizeof(*t->chunks) + ngx_rtmp_r32(t->chunks->entry_count) *
if (pos + sizeof(*t->chunks) + ngx_rtmp_r32(t->chunks->entry_count) *
sizeof(t->chunks->entries[0])
<= last)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: chunks entries=%uD",
"mp4: chunks entries=%uD",
ngx_rtmp_r32(t->chunks->entry_count));
return NGX_OK;
}
@ -1026,12 +1049,12 @@ ngx_rtmp_mp4_parse_stts(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
t->times = (ngx_rtmp_mp4_times_t *) pos;
if (pos + sizeof(*t->times) + ngx_rtmp_r32(t->times->entry_count) *
if (pos + sizeof(*t->times) + ngx_rtmp_r32(t->times->entry_count) *
sizeof(t->times->entries[0])
<= last)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: times entries=%uD",
"mp4: times entries=%uD",
ngx_rtmp_r32(t->times->entry_count));
return NGX_OK;
}
@ -1057,12 +1080,12 @@ ngx_rtmp_mp4_parse_ctts(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
t->delays = (ngx_rtmp_mp4_delays_t *) pos;
if (pos + sizeof(*t->delays) + ngx_rtmp_r32(t->delays->entry_count) *
if (pos + sizeof(*t->delays) + ngx_rtmp_r32(t->delays->entry_count) *
sizeof(t->delays->entries[0])
<= last)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: delays entries=%uD",
"mp4: delays entries=%uD",
ngx_rtmp_r32(t->delays->entry_count));
return NGX_OK;
}
@ -1088,12 +1111,12 @@ ngx_rtmp_mp4_parse_stss(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
t->keys = (ngx_rtmp_mp4_keys_t *) pos;
if (pos + sizeof(*t->keys) + ngx_rtmp_r32(t->keys->entry_count) *
if (pos + sizeof(*t->keys) + ngx_rtmp_r32(t->keys->entry_count) *
sizeof(t->keys->entries[0])
<= last)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: keys entries=%uD",
"mp4: keys entries=%uD",
ngx_rtmp_r32(t->keys->entry_count));
return NGX_OK;
}
@ -1121,7 +1144,7 @@ ngx_rtmp_mp4_parse_stsz(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
if (pos + sizeof(*t->sizes) <= last && t->sizes->sample_size) {
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: sizes size=%uD",
"mp4: sizes size=%uD",
ngx_rtmp_r32(t->sizes->sample_size));
return NGX_OK;
}
@ -1132,7 +1155,7 @@ ngx_rtmp_mp4_parse_stsz(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: sizes entries=%uD",
"mp4: sizes entries=%uD",
ngx_rtmp_r32(t->sizes->sample_count));
return NGX_OK;
}
@ -1163,8 +1186,8 @@ ngx_rtmp_mp4_parse_stz2(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
<= last)
{
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: sizes2 field_size=%uD entries=%uD",
ngx_rtmp_r32(t->sizes2->field_size),
"mp4: sizes2 field_size=%uD entries=%uD",
ngx_rtmp_r32(t->sizes2->field_size),
ngx_rtmp_r32(t->sizes2->sample_count));
return NGX_OK;
}
@ -1195,7 +1218,7 @@ ngx_rtmp_mp4_parse_stco(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
<= last)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: offsets entries=%uD",
"mp4: offsets entries=%uD",
ngx_rtmp_r32(t->offsets->entry_count));
return NGX_OK;
}
@ -1226,7 +1249,7 @@ ngx_rtmp_mp4_parse_co64(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
<= last)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: offsets64 entries=%uD",
"mp4: offsets64 entries=%uD",
ngx_rtmp_r32(t->offsets64->entry_count));
return NGX_OK;
}
@ -1257,7 +1280,7 @@ ngx_rtmp_mp4_parse(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
if (pos + size > last) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
"mp4: too big box '%*s': size=%uz",
"mp4: too big box '%*s': size=%uz",
4, &tag, size);
return NGX_ERROR;
}
@ -1266,7 +1289,7 @@ ngx_rtmp_mp4_parse(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
nboxes = sizeof(ngx_rtmp_mp4_boxes) / sizeof(ngx_rtmp_mp4_boxes[0]);
for (n = 0; n < nboxes && b->tag != tag; ++n, ++b);
if (n == nboxes) {
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: box unhandled '%*s'", 4, &tag);
@ -1313,7 +1336,7 @@ ngx_rtmp_mp4_next_time(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
ngx_log_debug8(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui time[%ui] [%ui/%uD][%ui/%uD]=%uD t=%uD",
t->id, cr->pos, cr->time_pos,
t->id, cr->pos, cr->time_pos,
ngx_rtmp_r32(t->times->entry_count),
cr->time_count, ngx_rtmp_r32(te->sample_count),
ngx_rtmp_r32(te->sample_delta),
@ -1332,7 +1355,7 @@ ngx_rtmp_mp4_next_time(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
static ngx_int_t
ngx_rtmp_mp4_seek_time(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t,
ngx_rtmp_mp4_seek_time(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t,
uint32_t timestamp)
{
ngx_rtmp_mp4_cursor_t *cr;
@ -1349,7 +1372,7 @@ ngx_rtmp_mp4_seek_time(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t,
while (cr->time_pos < ngx_rtmp_r32(t->times->entry_count)) {
dt = ngx_rtmp_r32(te->sample_delta) * ngx_rtmp_r32(te->sample_count);
if (cr->timestamp + dt >= timestamp) {
if (te->sample_delta == 0) {
return NGX_ERROR;
@ -1381,7 +1404,7 @@ ngx_rtmp_mp4_seek_time(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t,
ngx_log_debug8(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui seek time[%ui] [%ui/%uD][%ui/%uD]=%uD "
"t=%uD",
t->id, cr->pos, cr->time_pos,
t->id, cr->pos, cr->time_pos,
ngx_rtmp_r32(t->times->entry_count),
cr->time_count,
ngx_rtmp_r32(te->sample_count),
@ -1424,7 +1447,7 @@ ngx_rtmp_mp4_update_offset(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui offset[%ui/%uD]=%O",
t->id, cr->chunk,
t->id, cr->chunk,
ngx_rtmp_r32(t->offsets->entry_count),
cr->offset);
@ -1478,7 +1501,7 @@ ngx_rtmp_mp4_next_chunk(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
return NGX_ERROR;
}
ce = &t->chunks->entries[cr->chunk_pos];
cr->chunk_count++;
@ -1537,8 +1560,8 @@ ngx_rtmp_mp4_seek_chunk(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
while (cr->chunk_pos + 1 < ngx_rtmp_r32(t->chunks->entry_count)) {
nce = ce + 1;
dpos = (ngx_rtmp_r32(nce->first_chunk) -
dpos = (ngx_rtmp_r32(nce->first_chunk) -
ngx_rtmp_r32(ce->first_chunk)) *
ngx_rtmp_r32(ce->samples_per_chunk);
@ -1559,7 +1582,7 @@ ngx_rtmp_mp4_seek_chunk(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
cr->chunk = ngx_rtmp_r32(ce->first_chunk) + dchunk;
cr->chunk_pos = (ngx_uint_t) (ce - t->chunks->entries);
cr->chunk_count = (ngx_uint_t) (cr->pos - pos - dchunk *
cr->chunk_count = (ngx_uint_t) (cr->pos - pos - dchunk *
ngx_rtmp_r32(ce->samples_per_chunk));
ngx_log_debug7(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
@ -1609,7 +1632,7 @@ ngx_rtmp_mp4_next_size(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui size[%ui/%uD]=%uz",
t->id, cr->size_pos,
t->id, cr->size_pos,
ngx_rtmp_r32(t->sizes->sample_count),
cr->size);
@ -1678,7 +1701,7 @@ ngx_rtmp_mp4_seek_size(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui seek size[%ui/%uD]=%uz",
t->id, cr->size_pos,
t->id, cr->size_pos,
ngx_rtmp_r32(t->sizes->sample_count),
cr->size);
@ -1731,7 +1754,7 @@ ngx_rtmp_mp4_next_key(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
return NGX_OK;
}
ke = &t->keys->entries[cr->key_pos];
cr->key = (cr->pos + 1 == ngx_rtmp_r32(*ke));
@ -1870,7 +1893,7 @@ ngx_rtmp_mp4_seek_delay(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
while (cr->delay_pos < ngx_rtmp_r32(t->delays->entry_count)) {
dpos = ngx_rtmp_r32(de->sample_count);
if (pos + dpos > cr->pos) {
cr->delay_count = cr->pos - pos;
cr->delay = ngx_rtmp_r32(de->sample_offset);
@ -1943,46 +1966,46 @@ ngx_rtmp_mp4_send_meta(ngx_rtmp_session_t *s)
static ngx_rtmp_amf_elt_t out_inf[] = {
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("width"),
&v.width, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("height"),
&v.height, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("displayWidth"),
&v.width, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("displayHeight"),
&v.height, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("duration"),
&v.duration, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("videocodecid"),
&v.video_codec_id, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("audiocodecid"),
&v.audio_codec_id, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("audiosamplerate"),
&v.audio_sample_rate, 0 },
};
static ngx_rtmp_amf_elt_t out_elts[] = {
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
"onMetaData", 0 },
{ NGX_RTMP_AMF_OBJECT,
{ NGX_RTMP_AMF_OBJECT,
ngx_null_string,
out_inf, sizeof(out_inf) },
};
@ -2019,7 +2042,7 @@ ngx_rtmp_mp4_send_meta(ngx_rtmp_session_t *s)
}
out = NULL;
rc = ngx_rtmp_append_amf(s, &out, NULL, out_elts,
rc = ngx_rtmp_append_amf(s, &out, NULL, out_elts,
sizeof(out_elts) / sizeof(out_elts[0]));
if (rc != NGX_OK || out == NULL) {
return NGX_ERROR;
@ -2071,15 +2094,16 @@ ngx_rtmp_mp4_send(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_uint_t *ts)
ngx_rtmp_header_t h, lh;
ngx_rtmp_core_srv_conf_t *cscf;
ngx_chain_t *out, in;
ngx_rtmp_mp4_track_t *t;
ngx_rtmp_mp4_cursor_t *cr;
uint32_t buflen, end_timestamp, sched,
timestamp, last_timestamp, rdelay;
ngx_rtmp_mp4_track_t *t, *cur_t;
ngx_rtmp_mp4_cursor_t *cr, *cur_cr;
uint32_t buflen, end_timestamp,
timestamp, last_timestamp, rdelay,
cur_timestamp;
ssize_t ret;
u_char fhdr[5];
size_t fhdr_size;
ngx_int_t rc;
ngx_uint_t n, active;
ngx_uint_t n, counter;
cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
@ -2099,34 +2123,59 @@ ngx_rtmp_mp4_send(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_uint_t *ts)
return rc;
}
buflen = (s->buflen ? s->buflen + NGX_RTMP_MP4_BUFLEN_ADDON:
NGX_RTMP_MP4_DEFAULT_BUFLEN);
buflen = s->buflen + NGX_RTMP_MP4_BUFLEN_ADDON;
t = ctx->tracks;
sched = 0;
active = 0;
counter = 0;
last_timestamp = 0;
end_timestamp = ctx->start_timestamp +
end_timestamp = ctx->start_timestamp +
(ngx_current_msec - ctx->epoch) + buflen;
for (n = 0; n < ctx->ntracks; ++n, ++t) {
cr = &t->cursor;
if (!cr->valid) {
continue;
for ( ;; ) {
counter++;
if (counter > NGX_RTMP_MP4_MAX_FRAMES) {
return NGX_OK;
}
timestamp = ngx_rtmp_mp4_to_rtmp_timestamp(t, cr->timestamp);
timestamp = 0;
t = NULL;
for (n = 0; n < ctx->ntracks; n++) {
cur_t = &ctx->tracks[n];
cur_cr = &cur_t->cursor;
if (!cur_cr->valid) {
continue;
}
cur_timestamp = ngx_rtmp_mp4_to_rtmp_timestamp(cur_t,
cur_cr->timestamp);
if (t == NULL || cur_timestamp < timestamp) {
timestamp = cur_timestamp;
t = cur_t;
}
}
if (t == NULL) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"mp4: no track");
return NGX_DONE;
}
if (timestamp > end_timestamp) {
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui ahead %uD > %uD",
t->id, timestamp, end_timestamp);
goto next;
"mp4: track#%ui ahead %uD > %uD",
t->id, timestamp, end_timestamp);
if (ts) {
*ts = last_timestamp;
}
return (uint32_t) (timestamp - end_timestamp);
}
cr = &t->cursor;
last_timestamp = ngx_rtmp_mp4_to_rtmp_timestamp(t, cr->last_timestamp);
ngx_memzero(&h, sizeof(h));
@ -2150,7 +2199,7 @@ ngx_rtmp_mp4_send(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_uint_t *ts)
fhdr[0] = t->fhdr;
fhdr[1] = 0;
if (t->type == NGX_RTMP_MSG_VIDEO) {
fhdr[0] |= 0x10;
fhdr[2] = fhdr[3] = fhdr[4] = 0;
@ -2180,14 +2229,12 @@ ngx_rtmp_mp4_send(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_uint_t *ts)
}
t->header_sent = 1;
goto next;
}
ngx_log_debug5(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: track#%ui read frame offset=%O, size=%uz, "
"timestamp=%uD, last_timestamp=%uD",
t->id, cr->offset, cr->size, timestamp,
"timestamp=%uD, last_timestamp=%uD",
t->id, cr->offset, cr->size, timestamp,
last_timestamp);
ngx_rtmp_mp4_buffer[0] = t->fhdr;
@ -2204,7 +2251,7 @@ ngx_rtmp_mp4_send(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_uint_t *ts)
if (t->header) {
fhdr_size = 5;
rdelay = ngx_rtmp_mp4_to_rtmp_timestamp(t, cr->delay);
ngx_rtmp_mp4_buffer[1] = 1;
@ -2222,18 +2269,18 @@ ngx_rtmp_mp4_send(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_uint_t *ts)
if (cr->size + fhdr_size > sizeof(ngx_rtmp_mp4_buffer)) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"mp4: track#%ui too big frame: %D>%uz",
"mp4: track#%ui too big frame: %D>%uz",
t->id, cr->size, sizeof(ngx_rtmp_mp4_buffer));
continue;
goto next;
}
ret = ngx_read_file(f, ngx_rtmp_mp4_buffer + fhdr_size,
ret = ngx_read_file(f, ngx_rtmp_mp4_buffer + fhdr_size,
cr->size, cr->offset);
if (ret != (ssize_t) cr->size) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"mp4: track#%ui could not read frame", t->id);
continue;
goto next;
}
in.buf = &in_buf;
@ -2241,7 +2288,7 @@ ngx_rtmp_mp4_send(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_uint_t *ts)
in_buf.last = ngx_rtmp_mp4_buffer + cr->size + fhdr_size;
out = ngx_rtmp_append_shared_bufs(cscf, NULL, &in);
ngx_rtmp_prepare_message(s, &h, cr->not_first ? &lh : NULL, out);
rc = ngx_rtmp_send_message(s, out, 0);
ngx_rtmp_free_shared_chain(cscf, out);
@ -2250,35 +2297,13 @@ ngx_rtmp_mp4_send(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_uint_t *ts)
return NGX_AGAIN;
}
if (ngx_rtmp_mp4_next(s, t) != NGX_OK) {
continue;
}
s->current_time = timestamp;
next:
active = 1;
if (timestamp > end_timestamp &&
(sched == 0 || timestamp < end_timestamp + sched))
{
sched = (uint32_t) (timestamp - end_timestamp);
if (ngx_rtmp_mp4_next(s, t) != NGX_OK) {
return NGX_DONE;
}
}
if (sched) {
return sched;
}
if (active) {
return NGX_OK;
}
if (ts) {
*ts = last_timestamp;
}
/*ngx_rtmp_mp4_reset(s);*/
return NGX_DONE;
}
@ -2380,7 +2405,7 @@ ngx_rtmp_mp4_init(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_int_t aindex,
return NGX_ERROR;
}
return ngx_rtmp_mp4_parse(s, (u_char *) ctx->mmaped + page_offset,
return ngx_rtmp_mp4_parse(s, (u_char *) ctx->mmaped + page_offset,
(u_char *) ctx->mmaped + page_offset + size);
}
@ -2543,7 +2568,7 @@ ngx_rtmp_mp4_postconfiguration(ngx_conf_t *cf)
}
fmt = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_play_fmt_t));
if (fmt == NULL) {
return NGX_ERROR;
}

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -10,7 +11,7 @@
static ngx_int_t ngx_rtmp_netcall_postconfiguration(ngx_conf_t *cf);
static void * ngx_rtmp_netcall_create_srv_conf(ngx_conf_t *cf);
static char * ngx_rtmp_netcall_merge_srv_conf(ngx_conf_t *cf,
static char * ngx_rtmp_netcall_merge_srv_conf(ngx_conf_t *cf,
void *parent, void *child);
static void ngx_rtmp_netcall_close(ngx_connection_t *cc);
@ -131,7 +132,7 @@ ngx_rtmp_netcall_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
static ngx_int_t
ngx_rtmp_netcall_disconnect(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_rtmp_netcall_disconnect(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
ngx_rtmp_netcall_ctx_t *ctx;
@ -149,7 +150,7 @@ ngx_rtmp_netcall_disconnect(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
}
static ngx_int_t
static ngx_int_t
ngx_rtmp_netcall_get_peer(ngx_peer_connection_t *pc, void *data)
{
ngx_rtmp_netcall_session_t *cs = data;
@ -162,7 +163,7 @@ ngx_rtmp_netcall_get_peer(ngx_peer_connection_t *pc, void *data)
}
static void
static void
ngx_rtmp_netcall_free_peer(ngx_peer_connection_t *pc, void *data,
ngx_uint_t state)
{
@ -191,7 +192,7 @@ ngx_rtmp_netcall_create(ngx_rtmp_session_t *s, ngx_rtmp_netcall_init_t *ci)
/* get module context */
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_netcall_module);
if (ctx == NULL) {
ctx = ngx_pcalloc(c->pool,
ctx = ngx_pcalloc(c->pool,
sizeof(ngx_rtmp_netcall_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
@ -246,7 +247,7 @@ ngx_rtmp_netcall_create(ngx_rtmp_session_t *s, ngx_rtmp_netcall_init_t *ci)
/* connect */
rc = ngx_event_connect_peer(pc);
if (rc != NGX_OK && rc != NGX_AGAIN ) {
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"netcall: connection failed");
goto error;
}
@ -258,7 +259,7 @@ ngx_rtmp_netcall_create(ngx_rtmp_session_t *s, ngx_rtmp_netcall_init_t *ci)
cs->out = ci->create(s, ci->arg, pool);
if (cs->out == NULL) {
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"netcall: creation failed");
ngx_close_connection(pc->connection);
goto error;
@ -453,7 +454,7 @@ ngx_rtmp_netcall_send(ngx_event_t *wev)
}
if (wev->timedout) {
ngx_log_error(NGX_LOG_INFO, cc->log, NGX_ETIMEDOUT,
ngx_log_error(NGX_LOG_INFO, cc->log, NGX_ETIMEDOUT,
"netcall: client send timed out");
cc->timedout = 1;
ngx_rtmp_netcall_close(cc);
@ -545,7 +546,7 @@ ngx_rtmp_netcall_http_format_request(ngx_int_t method, ngx_str_t *host,
if (bl == NULL) {
return NULL;
}
b = ngx_create_temp_buf(pool, sizeof(rq_tmpl) + host->len +
content_type->len + NGX_SIZE_T_LEN);
if (b == NULL) {
@ -570,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;
@ -578,15 +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
);
/**
* @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;
}
@ -594,39 +615,49 @@ 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 = (u_char*) ngx_escape_uri(b->last, addr_text->data,
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);
b->last = ngx_cpymem(b->last, (u_char*) "&clientid=",
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;
}
ngx_chain_t *
ngx_chain_t *
ngx_rtmp_netcall_http_skip_header(ngx_chain_t *in)
{
ngx_buf_t *b;
@ -653,7 +684,7 @@ ngx_rtmp_netcall_http_skip_header(ngx_chain_t *in)
}
b = in->buf;
}
switch (*b->pos++) {
case '\r':
state = (state == lf) ? lfcr : normal;

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -17,7 +18,7 @@ typedef ngx_chain_t * (*ngx_rtmp_netcall_create_pt)(ngx_rtmp_session_t *s,
typedef ngx_int_t (*ngx_rtmp_netcall_filter_pt)(ngx_chain_t *in);
typedef ngx_int_t (*ngx_rtmp_netcall_sink_pt)(ngx_rtmp_session_t *s,
ngx_chain_t *in);
typedef ngx_int_t (*ngx_rtmp_netcall_handle_pt)(ngx_rtmp_session_t *s,
typedef ngx_int_t (*ngx_rtmp_netcall_handle_pt)(ngx_rtmp_session_t *s,
void *arg, ngx_chain_t *in);
#define NGX_RTMP_NETCALL_HTTP_GET 0
@ -44,12 +45,12 @@ typedef struct {
} ngx_rtmp_netcall_init_t;
ngx_int_t ngx_rtmp_netcall_create(ngx_rtmp_session_t *s,
ngx_int_t ngx_rtmp_netcall_create(ngx_rtmp_session_t *s,
ngx_rtmp_netcall_init_t *ci);
/* HTTP handling */
ngx_chain_t * ngx_rtmp_netcall_http_format_session(ngx_rtmp_session_t *s,
ngx_chain_t * ngx_rtmp_netcall_http_format_session(ngx_rtmp_session_t *s,
ngx_pool_t *pool);
ngx_chain_t * ngx_rtmp_netcall_http_format_request(ngx_int_t method,
ngx_str_t *host, ngx_str_t *uri, ngx_chain_t *args, ngx_chain_t *body,
@ -58,8 +59,8 @@ ngx_chain_t * ngx_rtmp_netcall_http_skip_header(ngx_chain_t *in);
/* Memcache handling */
ngx_chain_t * ngx_rtmp_netcall_memcache_set(ngx_rtmp_session_t *s,
ngx_pool_t *pool, ngx_str_t *key, ngx_str_t *value,
ngx_chain_t * ngx_rtmp_netcall_memcache_set(ngx_rtmp_session_t *s,
ngx_pool_t *pool, ngx_str_t *key, ngx_str_t *value,
ngx_uint_t flags, ngx_uint_t sec);

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,12 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <nginx.h>
#include "ngx_rtmp_play_module.h"
#include "ngx_rtmp_cmd_module.h"
#include "ngx_rtmp_netcall_module.h"
@ -22,7 +24,7 @@ static char *ngx_rtmp_play_url(ngx_conf_t *cf, ngx_command_t *cmd,
static void *ngx_rtmp_play_create_main_conf(ngx_conf_t *cf);
static ngx_int_t ngx_rtmp_play_postconfiguration(ngx_conf_t *cf);
static void * ngx_rtmp_play_create_app_conf(ngx_conf_t *cf);
static char * ngx_rtmp_play_merge_app_conf(ngx_conf_t *cf,
static char * ngx_rtmp_play_merge_app_conf(ngx_conf_t *cf,
void *parent, void *child);
static ngx_int_t ngx_rtmp_play_do_init(ngx_rtmp_session_t *s);
@ -121,7 +123,7 @@ ngx_rtmp_play_create_main_conf(ngx_conf_t *cf)
return NULL;
}
if (ngx_array_init(&pmcf->fmts, cf->pool, 1,
if (ngx_array_init(&pmcf->fmts, cf->pool, 1,
sizeof(ngx_rtmp_play_fmt_t *))
!= NGX_OK)
{
@ -289,7 +291,11 @@ ngx_rtmp_play_send(ngx_event_t *e)
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"play: send buffer full");
#if (nginx_version >= 1007012)
ngx_post_event(e, (ngx_queue_t *) &s->posted_dry_events);
#else
ngx_post_event(e, &s->posted_dry_events);
#endif
return;
}
@ -429,7 +435,12 @@ ngx_rtmp_play_do_stop(ngx_rtmp_session_t *s)
ngx_del_timer(&ctx->send_evt);
}
if (ctx->send_evt.prev) {
#if (nginx_version >= 1007005)
if (ctx->send_evt.posted)
#else
if (ctx->send_evt.prev)
#endif
{
ngx_delete_posted_event((&ctx->send_evt));
}
@ -474,6 +485,9 @@ ngx_rtmp_play_copy_local_file(ngx_rtmp_session_t *s, u_char *name)
ngx_rtmp_play_ctx_t *ctx;
u_char *path, *p;
static u_char dpath[NGX_MAX_PATH + 1];
u_char *d;
static u_char dir[NGX_MAX_PATH + 1];
u_int l;
pacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_play_module);
if (pacf == NULL) {
@ -489,6 +503,26 @@ ngx_rtmp_play_copy_local_file(ngx_rtmp_session_t *s, u_char *name)
p = ngx_snprintf(dpath, NGX_MAX_PATH, "%V/%s%V", &pacf->local_path,
name + ctx->pfx_size, &ctx->sfx);
d = name + ctx->pfx_size;
while (*d != '\0') {
if (*d == '/') {
p = ngx_snprintf(dir, NGX_MAX_PATH, "%V/%s", &pacf->local_path, name + ctx->pfx_size, &ctx->sfx);
l = ngx_strlen(dir) - ngx_strlen(d);
dir[l]='\0';
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"play: create dir '%s' for '%s'", dir, dpath);
if (ngx_create_dir(dir, 0700) == NGX_FILE_ERROR) {
if (ngx_errno != NGX_EEXIST) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
"play: error creating dir '%s' for '%s'", dir, dpath);
break;
}
}
}
d++;
}
*p = 0;
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
@ -682,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;
@ -715,8 +752,8 @@ ngx_rtmp_play_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
* we should not move out of play directory */
for (p = v->name; *p; ++p) {
if (ngx_path_separator(p[0]) &&
p[1] == '.' && p[2] == '.' &&
ngx_path_separator(p[3]))
p[1] == '.' && p[2] == '.' &&
ngx_path_separator(p[3]))
{
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"play: bad name '%s'", v->name);
@ -734,7 +771,7 @@ ngx_rtmp_play_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
ctx->session = s;
ctx->aindex = ngx_rtmp_play_parse_index('a', v->args);
ctx->vindex = ngx_rtmp_play_parse_index('v', v->args);
ctx->file.log = s->connection->log;
ngx_memcpy(ctx->name, v->name, NGX_RTMP_MAX_NAME);
@ -743,7 +780,7 @@ ngx_rtmp_play_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
name.data = v->name;
pfmt = pmcf->fmts.elts;
for (n = 0; n < pmcf->fmts.nelts; ++n, ++pfmt) {
fmt = *pfmt;
@ -764,7 +801,7 @@ ngx_rtmp_play_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
}
if (name.len >= sfx->len &&
ngx_strncasecmp(sfx->data, name.data + name.len - sfx->len,
ngx_strncasecmp(sfx->data, name.data + name.len - sfx->len,
sfx->len) == 0)
{
ctx->fmt = fmt;
@ -784,7 +821,7 @@ ngx_rtmp_play_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
sfx = &ctx->fmt->sfx;
if (name.len < sfx->len ||
ngx_strncasecmp(sfx->data, name.data + name.len - sfx->len,
ngx_strncasecmp(sfx->data, name.data + name.len - sfx->len,
sfx->len))
{
ctx->sfx = *sfx;
@ -793,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);
}
@ -803,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;
@ -847,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);
}
@ -856,12 +905,26 @@ ngx_rtmp_play_next_entry(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
pe->root, v->name + ctx->pfx_size, &ctx->sfx);
*p = 0;
ctx->file.fd = ngx_open_file(path, NGX_FILE_RDONLY, NGX_FILE_OPEN,
ctx->file.fd = ngx_open_file(path, NGX_FILE_RDONLY, NGX_FILE_OPEN,
NGX_FILE_DEFAULT_ACCESS);
/* try unsuffixed file name as fallback if adding suffix didn't work */
if (ctx->file.fd == NGX_INVALID_FILE) {
ngx_log_debug1(NGX_LOG_ERR, s->connection->log, ngx_errno,
"play: error opening file '%s'", path);
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, ngx_errno,
"play: error opening file '%s', trying without suffix",
path);
p = ngx_snprintf(path, NGX_MAX_PATH, "%V/%s",
pe->root, v->name + ctx->pfx_size);
*p = 0;
ctx->file.fd = ngx_open_file(path, NGX_FILE_RDONLY, NGX_FILE_OPEN,
NGX_FILE_DEFAULT_ACCESS);
}
if (ctx->file.fd == NGX_INVALID_FILE) {
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, ngx_errno,
"play: error opening fallback file '%s'", path);
continue;
}
@ -869,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);
}
@ -914,6 +984,10 @@ ngx_rtmp_play_open(ngx_rtmp_session_t *s, double start)
ngx_rtmp_send_recorded(s, 1);
if (ngx_rtmp_send_sample_access(s) != NGX_OK) {
return NGX_ERROR;
}
if (ngx_rtmp_play_do_init(s) != NGX_OK) {
return NGX_ERROR;
}
@ -993,9 +1067,12 @@ ngx_rtmp_play_remote_create(ngx_rtmp_session_t *s, void *arg, ngx_pool_t *pool)
}
static ngx_int_t
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;
@ -1003,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);
}
@ -1014,14 +1093,21 @@ 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);
}
static ngx_int_t
static ngx_int_t
ngx_rtmp_play_remote_sink(ngx_rtmp_session_t *s, ngx_chain_t *in)
{
ngx_rtmp_play_ctx_t *ctx;
@ -1045,7 +1131,7 @@ ngx_rtmp_play_remote_sink(ngx_rtmp_session_t *s, ngx_chain_t *in)
}
/* 10th header byte is HTTP response header */
if (++ctx->nheader == 10 && *b->pos != (u_char) '2') {
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"play: remote HTTP response code: %cxx",
*b->pos);
return NGX_ERROR;
@ -1068,7 +1154,7 @@ ngx_rtmp_play_remote_sink(ngx_rtmp_session_t *s, ngx_chain_t *in)
rc = ngx_write_fd(ctx->file.fd, b->pos, b->last - b->pos);
if (rc == NGX_ERROR) {
ngx_log_error(NGX_LOG_INFO, s->connection->log, ngx_errno,
ngx_log_error(NGX_LOG_INFO, s->connection->log, ngx_errno,
"play: error writing to temp file");
return NGX_ERROR;
}
@ -1208,7 +1294,7 @@ ngx_rtmp_play_url(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
if (pe->root == NULL) {
return NGX_CONF_ERROR;
}
*pe->root = value[n];
continue;

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -13,13 +14,13 @@
#include "ngx_rtmp_cmd_module.h"
typedef ngx_int_t (*ngx_rtmp_play_init_pt) (ngx_rtmp_session_t *s,
typedef ngx_int_t (*ngx_rtmp_play_init_pt) (ngx_rtmp_session_t *s,
ngx_file_t *f, ngx_int_t aindex, ngx_int_t vindex);
typedef ngx_int_t (*ngx_rtmp_play_done_pt) (ngx_rtmp_session_t *s,
ngx_file_t *f);
typedef ngx_int_t (*ngx_rtmp_play_start_pt) (ngx_rtmp_session_t *s,
typedef ngx_int_t (*ngx_rtmp_play_start_pt) (ngx_rtmp_session_t *s,
ngx_file_t *f);
typedef ngx_int_t (*ngx_rtmp_play_seek_pt) (ngx_rtmp_session_t *s,
typedef ngx_int_t (*ngx_rtmp_play_seek_pt) (ngx_rtmp_session_t *s,
ngx_file_t *f, ngx_uint_t offs);
typedef ngx_int_t (*ngx_rtmp_play_stop_pt) (ngx_rtmp_session_t *s,
ngx_file_t *f);

197
ngx_rtmp_proxy_protocol.c Normal file
View file

@ -0,0 +1,197 @@
/*
* Copyright (C) Roman Arutyunyan
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <nginx.h>
#include "ngx_rtmp_proxy_protocol.h"
static void ngx_rtmp_proxy_protocol_recv(ngx_event_t *rev);
void
ngx_rtmp_proxy_protocol(ngx_rtmp_session_t *s)
{
ngx_event_t *rev;
ngx_connection_t *c;
c = s->connection;
rev = c->read;
rev->handler = ngx_rtmp_proxy_protocol_recv;
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"proxy_protocol: start");
if (rev->ready) {
/* the deferred accept(), rtsig, aio, iocp */
if (ngx_use_accept_mutex) {
ngx_post_event(rev, &ngx_posted_events);
return;
}
rev->handler(rev);
return;
}
ngx_add_timer(rev, s->timeout);
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
ngx_rtmp_finalize_session(s);
return;
}
}
static void
ngx_rtmp_proxy_protocol_recv(ngx_event_t *rev)
{
u_char buf[107], *p, *pp, *text;
size_t len;
ssize_t n;
ngx_err_t err;
ngx_int_t i;
ngx_addr_t addr;
ngx_connection_t *c;
ngx_rtmp_session_t *s;
c = rev->data;
s = c->data;
if (c->destroyed) {
return;
}
if (rev->timedout) {
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
"proxy_protocol: recv: client timed out");
c->timedout = 1;
ngx_rtmp_finalize_session(s);
return;
}
if (rev->timer_set) {
ngx_del_timer(rev);
}
n = recv(c->fd, (char *) buf, sizeof(buf), MSG_PEEK);
err = ngx_socket_errno;
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "recv(): %d", n);
if (n == -1) {
if (err == NGX_EAGAIN) {
ngx_add_timer(rev, s->timeout);
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
ngx_rtmp_finalize_session(s);
}
return;
}
ngx_rtmp_finalize_session(s);
return;
}
p = buf;
if (n <= 8 && ngx_strncmp(p, "PROXY ", 6) != 0) {
goto bad_header;
}
n -= 6;
p += 6;
ngx_memzero(&addr, sizeof(ngx_addr_t));
if (n >= 7 && ngx_strncmp(p, "UNKNOWN", 7) == 0) {
n -= 7;
p += 7;
goto skip;
}
if (n < 5 || ngx_strncmp(p, "TCP", 3) != 0
|| (p[3] != '4' && p[3] != '6') || p[4] != ' ')
{
goto bad_header;
}
n -= 5;
p += 5;
pp = ngx_strlchr(p, p + n, ' ');
if (pp == NULL) {
goto bad_header;
}
if (ngx_parse_addr(s->connection->pool, &addr, p, pp - p) != NGX_OK) {
goto bad_header;
}
n -= pp - p;
p = pp;
skip:
for (i = 0; i + 1 < n; i++) {
if (p[i] == CR && p[i + 1] == LF) {
break;
}
}
if (i + 1 >= n) {
goto bad_header;
}
n = p - buf + i + 2;
if (c->recv(c, buf, n) != n) {
goto failed;
}
if (addr.socklen) {
text = ngx_palloc(s->connection->pool, NGX_SOCKADDR_STRLEN);
if (text == NULL) {
goto failed;
}
len = ngx_sock_ntop(addr.sockaddr,
#if (nginx_version >= 1005003)
addr.socklen,
#endif
text, NGX_SOCKADDR_STRLEN, 0);
if (len == 0) {
goto failed;
}
c->sockaddr = addr.sockaddr;
c->socklen = addr.socklen;
c->addr_text.data = text;
c->addr_text.len = len;
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0,
"proxy_protocol: remote_addr:'%V'", &c->addr_text);
}
ngx_rtmp_handshake(s);
return;
bad_header:
ngx_log_error(NGX_LOG_INFO, c->log, 0, "proxy_protocol: bad header");
failed:
ngx_rtmp_finalize_session(s);
}

19
ngx_rtmp_proxy_protocol.h Normal file
View file

@ -0,0 +1,19 @@
/*
* Copyright (C) Roman Arutyunyan
*/
#ifndef _NGX_RTMP_PROXY_PROTOCOL_H_INCLUDED_
#define _NGX_RTMP_PROXY_PROTOCOL_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include "ngx_rtmp.h"
void ngx_rtmp_proxy_protocol(ngx_rtmp_session_t *c);
#endif /* _NGX_RTMP_PROXY_PROTOCOL_H_INCLUDED_ */

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -11,12 +12,12 @@
#include <string.h>
ngx_int_t
ngx_int_t
ngx_rtmp_protocol_message_handler(ngx_rtmp_session_t *s,
ngx_rtmp_header_t *h, ngx_chain_t *in)
{
ngx_buf_t *b;
u_char *p;
u_char *p;
uint32_t val;
uint8_t limit;
@ -66,7 +67,7 @@ ngx_rtmp_protocol_message_handler(ngx_rtmp_session_t *s,
(void)limit;
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"receive bandwidth=%uD limit=%d",
"receive bandwidth=%uD limit=%d",
val, (int)limit);
/* receive window size =val
@ -82,12 +83,12 @@ ngx_rtmp_protocol_message_handler(ngx_rtmp_session_t *s,
}
ngx_int_t
ngx_int_t
ngx_rtmp_user_message_handler(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
ngx_buf_t *b;
u_char *p;
u_char *p;
uint16_t evt;
uint32_t val;
@ -106,7 +107,7 @@ ngx_rtmp_user_message_handler(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
p[1] = b->pos[0];
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"RTMP recv user evt %s (%i)",
"RTMP recv user evt %s (%i)",
ngx_rtmp_user_message_type(evt), (ngx_int_t) evt);
p = (u_char *) &val;
@ -255,7 +256,7 @@ ngx_rtmp_fetch_uint32(ngx_chain_t **in, uint32_t *ret, ngx_int_t n)
}
ngx_int_t
ngx_int_t
ngx_rtmp_aggregate_message_handler(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
@ -321,7 +322,7 @@ ngx_rtmp_aggregate_message_handler(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
}
if (cl == NULL) {
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"RTMP error parsing aggregate");
return NGX_ERROR;
}
@ -363,7 +364,7 @@ ngx_rtmp_aggregate_message_handler(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
}
ngx_int_t
ngx_int_t
ngx_rtmp_amf_message_handler(ngx_rtmp_session_t *s,
ngx_rtmp_header_t *h, ngx_chain_t *in)
{
@ -378,14 +379,14 @@ ngx_rtmp_amf_message_handler(ngx_rtmp_session_t *s,
static ngx_rtmp_amf_elt_t elts[] = {
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
ngx_null_string,
func, sizeof(func) },
};
/* AMF command names come with string type, but shared object names
* come without type */
if (h->type == NGX_RTMP_MSG_AMF_SHARED ||
h->type == NGX_RTMP_MSG_AMF3_SHARED)
if (h->type == NGX_RTMP_MSG_AMF_SHARED ||
h->type == NGX_RTMP_MSG_AMF3_SHARED)
{
elts[0].type |= NGX_RTMP_AMF_TYPELESS;
} else {
@ -395,7 +396,7 @@ ngx_rtmp_amf_message_handler(ngx_rtmp_session_t *s,
if ((h->type == NGX_RTMP_MSG_AMF3_SHARED ||
h->type == NGX_RTMP_MSG_AMF3_META ||
h->type == NGX_RTMP_MSG_AMF3_CMD)
&& in->buf->last > in->buf->pos)
&& in->buf->last > in->buf->pos)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"AMF3 prefix: %ui", (ngx_int_t)*in->buf->pos);
@ -410,8 +411,8 @@ ngx_rtmp_amf_message_handler(ngx_rtmp_session_t *s,
act.log = s->connection->log;
memset(func, 0, sizeof(func));
if (ngx_rtmp_amf_read(&act, elts,
sizeof(elts) / sizeof(elts[0])) != NGX_OK)
if (ngx_rtmp_amf_read(&act, elts,
sizeof(elts) / sizeof(elts[0])) != NGX_OK)
{
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"AMF cmd failed");
@ -424,14 +425,14 @@ ngx_rtmp_amf_message_handler(ngx_rtmp_session_t *s,
len = ngx_strlen(func);
ch = ngx_hash_find(&cmcf->amf_hash,
ch = ngx_hash_find(&cmcf->amf_hash,
ngx_hash_strlow(func, func, len), func, len);
if (ch && ch->nelts) {
ph = ch->elts;
for (n = 0; n < ch->nelts; ++n, ++ph) {
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"AMF func '%s' passed to handler %d/%d",
"AMF func '%s' passed to handler %d/%d",
func, n, ch->nelts);
switch ((*ph)(s, h, in)) {
case NGX_ERROR:
@ -460,4 +461,4 @@ ngx_rtmp_receive_amf(ngx_rtmp_session_t *s, ngx_chain_t *in,
act.log = s->connection->log;
return ngx_rtmp_amf_read(&act, elts, nelts);
}
}

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -12,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;
@ -25,20 +27,20 @@ static char *ngx_rtmp_record_recorder(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_int_t ngx_rtmp_record_postconfiguration(ngx_conf_t *cf);
static void * ngx_rtmp_record_create_app_conf(ngx_conf_t *cf);
static char * ngx_rtmp_record_merge_app_conf(ngx_conf_t *cf,
static char * ngx_rtmp_record_merge_app_conf(ngx_conf_t *cf,
void *parent, void *child);
static ngx_int_t ngx_rtmp_record_write_frame(ngx_rtmp_session_t *s,
static ngx_int_t ngx_rtmp_record_write_frame(ngx_rtmp_session_t *s,
ngx_rtmp_record_rec_ctx_t *rctx,
ngx_rtmp_header_t *h, ngx_chain_t *in, ngx_int_t inc_nframes);
static ngx_int_t ngx_rtmp_record_av(ngx_rtmp_session_t *s,
static ngx_int_t ngx_rtmp_record_avd(ngx_rtmp_session_t *s,
ngx_rtmp_header_t *h, ngx_chain_t *in);
static ngx_int_t ngx_rtmp_record_node_av(ngx_rtmp_session_t *s,
static ngx_int_t ngx_rtmp_record_node_avd(ngx_rtmp_session_t *s,
ngx_rtmp_record_rec_ctx_t *rctx, ngx_rtmp_header_t *h, ngx_chain_t *in);
static ngx_int_t ngx_rtmp_record_node_open(ngx_rtmp_session_t *s,
ngx_rtmp_record_rec_ctx_t *rctx);
static ngx_int_t ngx_rtmp_record_node_close(ngx_rtmp_session_t *s,
ngx_rtmp_record_rec_ctx_t *rctx);
static void ngx_rtmp_record_make_path(ngx_rtmp_session_t *s,
static void ngx_rtmp_record_make_path(ngx_rtmp_session_t *s,
ngx_rtmp_record_rec_ctx_t *rctx, ngx_str_t *path);
static ngx_int_t ngx_rtmp_record_init(ngx_rtmp_session_t *s);
@ -46,9 +48,13 @@ static ngx_int_t ngx_rtmp_record_init(ngx_rtmp_session_t *s);
static ngx_conf_bitmask_t ngx_rtmp_record_mask[] = {
{ ngx_string("off"), NGX_RTMP_RECORD_OFF },
{ ngx_string("all"), NGX_RTMP_RECORD_AUDIO |
NGX_RTMP_RECORD_VIDEO |
NGX_RTMP_RECORD_DATA },
{ ngx_string("av"), NGX_RTMP_RECORD_AUDIO |
NGX_RTMP_RECORD_VIDEO },
{ ngx_string("audio"), NGX_RTMP_RECORD_AUDIO },
{ ngx_string("video"), NGX_RTMP_RECORD_VIDEO },
{ ngx_string("data"), NGX_RTMP_RECORD_DATA },
{ ngx_string("keyframes"), NGX_RTMP_RECORD_KEYFRAMES },
{ ngx_string("manual"), NGX_RTMP_RECORD_MANUAL },
{ ngx_null_string, 0 }
@ -105,6 +111,14 @@ static ngx_command_t ngx_rtmp_record_commands[] = {
offsetof(ngx_rtmp_record_app_conf_t, lock_file),
NULL },
{ 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_conf_set_size_slot,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_rtmp_record_app_conf_t, interval_size),
NULL },
{ ngx_string("record_max_size"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|
NGX_RTMP_REC_CONF|NGX_CONF_TAKE1,
@ -189,6 +203,7 @@ ngx_rtmp_record_create_app_conf(ngx_conf_t *cf)
}
racf->max_size = NGX_CONF_UNSET_SIZE;
racf->interval_size = NGX_CONF_UNSET_SIZE;
racf->max_frames = NGX_CONF_UNSET_SIZE;
racf->interval = NGX_CONF_UNSET_MSEC;
racf->unique = NGX_CONF_UNSET;
@ -215,12 +230,13 @@ ngx_rtmp_record_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_str_value(conf->path, prev->path, "");
ngx_conf_merge_str_value(conf->suffix, prev->suffix, ".flv");
ngx_conf_merge_size_value(conf->max_size, prev->max_size, 0);
ngx_conf_merge_size_value(conf->interval_size, prev->interval_size, 0);
ngx_conf_merge_size_value(conf->max_frames, prev->max_frames, 0);
ngx_conf_merge_value(conf->unique, prev->unique, 0);
ngx_conf_merge_value(conf->append, prev->append, 0);
ngx_conf_merge_value(conf->lock_file, prev->lock_file, 0);
ngx_conf_merge_value(conf->notify, prev->notify, 0);
ngx_conf_merge_msec_value(conf->interval, prev->interval,
ngx_conf_merge_msec_value(conf->interval, prev->interval,
(ngx_msec_t) NGX_CONF_UNSET);
ngx_conf_merge_bitmask_value(conf->flags, prev->flags, 0);
ngx_conf_merge_ptr_value(conf->url, prev->url, NULL);
@ -247,9 +263,9 @@ ngx_rtmp_record_write_header(ngx_file_t *file)
0x56, /* 'V' */
0x01, /* version = 1 */
0x05, /* 00000 1 0 1 = has audio & video */
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x09, /* header size */
0x00,
0x00,
@ -291,7 +307,7 @@ ngx_rtmp_record_open(ngx_rtmp_session_t *s, ngx_uint_t n, ngx_str_t *path)
ngx_rtmp_record_rec_ctx_t *rctx;
ngx_int_t rc;
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"record: #%ui manual open", n);
rctx = ngx_rtmp_record_get_node_ctx(s, n);
@ -319,7 +335,7 @@ ngx_rtmp_record_close(ngx_rtmp_session_t *s, ngx_uint_t n, ngx_str_t *path)
ngx_rtmp_record_rec_ctx_t *rctx;
ngx_int_t rc;
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"record: #%ui manual close", n);
rctx = ngx_rtmp_record_get_node_ctx(s, n);
@ -384,7 +400,7 @@ ngx_rtmp_record_make_path(ngx_rtmp_session_t *s,
p = pbuf;
l = pbuf + sizeof(pbuf) - 1;
p = ngx_cpymem(p, rracf->path.data,
p = ngx_cpymem(p, rracf->path.data,
ngx_min(rracf->path.len, (size_t)(l - p - 1)));
*p++ = '/';
p = (u_char *)ngx_escape_uri(p, ctx->name, ngx_min(ngx_strlen(ctx->name),
@ -392,7 +408,7 @@ ngx_rtmp_record_make_path(ngx_rtmp_session_t *s,
/* append timestamp */
if (rracf->unique) {
p = ngx_cpymem(p, buf, ngx_min(ngx_sprintf(buf, "-%T",
p = ngx_cpymem(p, buf, ngx_min(ngx_sprintf(buf, "-%T",
rctx->timestamp) - buf, l - p));
}
@ -400,7 +416,7 @@ ngx_rtmp_record_make_path(ngx_rtmp_session_t *s,
ngx_libc_localtime(rctx->timestamp, &tm);
p += strftime((char *) p, l - p, (char *) rracf->suffix.data, &tm);
} else {
p = ngx_cpymem(p, rracf->suffix.data,
p = ngx_cpymem(p, rracf->suffix.data,
ngx_min(rracf->suffix.len, (size_t)(l - p)));
}
@ -408,7 +424,7 @@ ngx_rtmp_record_make_path(ngx_rtmp_session_t *s,
path->data = pbuf;
path->len = p - pbuf;
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"record: %V path: '%V'", &rracf->id, path);
}
@ -431,7 +447,7 @@ ngx_rtmp_record_notify_error(ngx_rtmp_session_t *s,
static ngx_int_t
ngx_rtmp_record_node_open(ngx_rtmp_session_t *s,
ngx_rtmp_record_node_open(ngx_rtmp_session_t *s,
ngx_rtmp_record_rec_ctx_t *rctx)
{
ngx_rtmp_record_app_conf_t *rracf;
@ -449,7 +465,7 @@ ngx_rtmp_record_node_open(ngx_rtmp_session_t *s,
return NGX_AGAIN;
}
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"record: %V opening", &rracf->id);
ngx_memzero(rctx, sizeof(*rctx));
@ -493,7 +509,7 @@ ngx_rtmp_record_node_open(ngx_rtmp_session_t *s,
}
#endif
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"record: %V opened '%V'", &rracf->id, &path);
if (rracf->notify) {
@ -719,7 +735,7 @@ ngx_rtmp_record_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
return NGX_ERROR;
}
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"record: publish %ui nodes",
racf->rec.nelts);
@ -731,8 +747,8 @@ ngx_rtmp_record_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
/* terminate name on /../ */
for (p = ctx->name; *p; ++p) {
if (ngx_path_separator(p[0]) &&
p[1] == '.' && p[2] == '.' &&
ngx_path_separator(p[3]))
p[1] == '.' && p[2] == '.' &&
ngx_path_separator(p[3]))
{
*p = 0;
break;
@ -789,6 +805,7 @@ ngx_rtmp_record_node_close(ngx_rtmp_session_t *s,
void **app_conf;
ngx_int_t rc;
ngx_rtmp_record_done_t v;
u_char av;
rracf = rctx->conf;
@ -796,6 +813,23 @@ ngx_rtmp_record_node_close(ngx_rtmp_session_t *s,
return NGX_AGAIN;
}
if (rctx->initialized) {
av = 0;
if (rctx->video) {
av |= 0x01;
}
if (rctx->audio) {
av |= 0x04;
}
if (ngx_write_file(&rctx->file, &av, 1, 4) == NGX_ERROR) {
ngx_log_error(NGX_LOG_CRIT, s->connection->log, ngx_errno,
"record: %V error writing av mask", &rracf->id);
}
}
if (ngx_close_file(rctx->file.fd) == NGX_FILE_ERROR) {
err = ngx_errno;
ngx_log_error(NGX_LOG_CRIT, s->connection->log, err,
@ -806,7 +840,7 @@ ngx_rtmp_record_node_close(ngx_rtmp_session_t *s,
rctx->file.fd = NGX_INVALID_FILE;
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"record: %V closed", &rracf->id);
if (rracf->notify) {
@ -823,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;
@ -832,14 +868,14 @@ ngx_rtmp_record_node_close(ngx_rtmp_session_t *s,
static ngx_int_t
ngx_rtmp_record_close_stream(ngx_rtmp_session_t *s,
ngx_rtmp_record_close_stream(ngx_rtmp_session_t *s,
ngx_rtmp_close_stream_t *v)
{
if (s->auto_pushed) {
goto next;
}
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"record: close_stream");
ngx_rtmp_record_stop(s);
@ -850,7 +886,7 @@ next:
static ngx_int_t
ngx_rtmp_record_write_frame(ngx_rtmp_session_t *s,
ngx_rtmp_record_write_frame(ngx_rtmp_session_t *s,
ngx_rtmp_record_rec_ctx_t *rctx,
ngx_rtmp_header_t *h, ngx_chain_t *in,
ngx_int_t inc_nframes)
@ -865,8 +901,37 @@ ngx_rtmp_record_write_frame(ngx_rtmp_session_t *s,
"record: %V frame: mlen=%uD",
&rracf->id, h->mlen);
if (h->type == NGX_RTMP_MSG_VIDEO) {
rctx->video = 1;
}
if (h->type == NGX_RTMP_MSG_AUDIO) {
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) {
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"record: %V cut timestamp=%D", &rracf->id, timestamp);
timestamp = 0;
}
/* write tag header */
ph = hdr;
@ -890,7 +955,7 @@ ngx_rtmp_record_write_frame(ngx_rtmp_session_t *s,
tag_size = (ph - hdr) + h->mlen;
if (ngx_write_file(&rctx->file, hdr, ph - hdr, rctx->file.offset)
== NGX_ERROR)
== NGX_ERROR)
{
ngx_rtmp_record_notify_error(s, rctx);
@ -910,7 +975,7 @@ ngx_rtmp_record_write_frame(ngx_rtmp_session_t *s,
continue;
}
if (ngx_write_file(&rctx->file, in->buf->pos, in->buf->last
if (ngx_write_file(&rctx->file, in->buf->pos, in->buf->last
- in->buf->pos, rctx->file.offset)
== NGX_ERROR)
{
@ -927,9 +992,9 @@ ngx_rtmp_record_write_frame(ngx_rtmp_session_t *s,
*ph++ = p[1];
*ph++ = p[0];
if (ngx_write_file(&rctx->file, hdr, ph - hdr,
if (ngx_write_file(&rctx->file, hdr, ph - hdr,
rctx->file.offset)
== NGX_ERROR)
== NGX_ERROR)
{
return NGX_ERROR;
}
@ -961,7 +1026,7 @@ ngx_rtmp_record_get_chain_mlen(ngx_chain_t *in)
static ngx_int_t
ngx_rtmp_record_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_rtmp_record_avd(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
ngx_rtmp_record_ctx_t *ctx;
@ -977,7 +1042,7 @@ ngx_rtmp_record_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
rctx = ctx->rec.elts;
for (n = 0; n < ctx->rec.nelts; ++n, ++rctx) {
ngx_rtmp_record_node_av(s, rctx, h, in);
ngx_rtmp_record_node_avd(s, rctx, h, in);
}
return NGX_OK;
@ -985,7 +1050,7 @@ ngx_rtmp_record_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
static ngx_int_t
ngx_rtmp_record_node_av(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx,
ngx_rtmp_record_node_avd(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx,
ngx_rtmp_header_t *h, ngx_chain_t *in)
{
ngx_time_t next;
@ -1009,37 +1074,15 @@ ngx_rtmp_record_node_av(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx,
? keyframe
: (rracf->flags & NGX_RTMP_RECORD_VIDEO) == 0;
if (brkframe && (rracf->flags & NGX_RTMP_RECORD_MANUAL) == 0) {
if (rracf->interval != (ngx_msec_t) NGX_CONF_UNSET) {
next = rctx->last;
next.msec += rracf->interval;
next.sec += (next.msec / 1000);
next.msec %= 1000;
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_open(s, rctx);
}
}
if ((rracf->flags & NGX_RTMP_RECORD_MANUAL) &&
!brkframe && rctx->nframes == 0)
{
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)
@ -1047,6 +1090,12 @@ ngx_rtmp_record_node_av(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx,
return NGX_OK;
}
if (h->type == NGX_RTMP_MSG_AMF_META &&
(rracf->flags & NGX_RTMP_RECORD_DATA) == 0)
{
return NGX_OK;
}
if (h->type == NGX_RTMP_MSG_VIDEO &&
(rracf->flags & NGX_RTMP_RECORD_VIDEO) == 0 &&
((rracf->flags & NGX_RTMP_RECORD_KEYFRAMES) == 0 || !keyframe))
@ -1054,10 +1103,31 @@ ngx_rtmp_record_node_av(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx,
return NGX_OK;
}
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);
next.msec %= 1000;
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_open(s, rctx);
}
if (!rctx->initialized) {
rctx->initialized = 1;
rctx->epoch = h->timestamp + rctx->time_shift;
rctx->epoch = h->timestamp - rctx->time_shift;
if (rctx->file.offset == 0 &&
ngx_rtmp_record_write_header(&rctx->file) != NGX_OK)
@ -1075,7 +1145,7 @@ ngx_rtmp_record_node_av(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx,
if (!rctx->aac_header_sent && codec_ctx->aac_header &&
(rracf->flags & NGX_RTMP_RECORD_AUDIO))
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"record: %V writing AAC header", &rracf->id);
ch.type = NGX_RTMP_MSG_AUDIO;
@ -1083,7 +1153,7 @@ ngx_rtmp_record_node_av(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx,
if (ngx_rtmp_record_write_frame(s, rctx, &ch,
codec_ctx->aac_header, 0)
!= NGX_OK)
!= NGX_OK)
{
return NGX_OK;
}
@ -1092,11 +1162,11 @@ ngx_rtmp_record_node_av(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx,
}
/* AVC header */
if (!rctx->avc_header_sent && codec_ctx->avc_header &&
if (!rctx->avc_header_sent && codec_ctx->avc_header &&
(rracf->flags & (NGX_RTMP_RECORD_VIDEO|
NGX_RTMP_RECORD_KEYFRAMES)))
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"record: %V writing AVC header", &rracf->id);
ch.type = NGX_RTMP_MSG_VIDEO;
@ -1104,7 +1174,7 @@ ngx_rtmp_record_node_av(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx,
if (ngx_rtmp_record_write_frame(s, rctx, &ch,
codec_ctx->avc_header, 0)
!= NGX_OK)
!= NGX_OK)
{
return NGX_OK;
}
@ -1117,7 +1187,7 @@ ngx_rtmp_record_node_av(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx,
if (codec_ctx && codec_ctx->video_codec_id == NGX_RTMP_VIDEO_H264 &&
!rctx->avc_header_sent)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"record: %V skipping until H264 header", &rracf->id);
return NGX_OK;
}
@ -1128,9 +1198,9 @@ ngx_rtmp_record_node_av(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx,
{
rctx->video_key_sent = 1;
}
if (!rctx->video_key_sent) {
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"record: %V skipping until keyframe", &rracf->id);
return NGX_OK;
}
@ -1139,7 +1209,7 @@ ngx_rtmp_record_node_av(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx,
if (codec_ctx && codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC &&
!rctx->aac_header_sent)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"record: %V skipping until AAC header", &rracf->id);
return NGX_OK;
}
@ -1149,6 +1219,12 @@ ngx_rtmp_record_node_av(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)
{
@ -1163,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;
@ -1189,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;
}
}
@ -1243,15 +1325,20 @@ 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);
h = ngx_array_push(&cmcf->events[NGX_RTMP_MSG_AUDIO]);
*h = ngx_rtmp_record_av;
*h = ngx_rtmp_record_avd;
h = ngx_array_push(&cmcf->events[NGX_RTMP_MSG_VIDEO]);
*h = ngx_rtmp_record_av;
*h = ngx_rtmp_record_avd;
h = ngx_array_push(&cmcf->events[NGX_RTMP_MSG_AMF_META]);
*h = ngx_rtmp_record_avd;
next_publish = ngx_rtmp_publish;
ngx_rtmp_publish = ngx_rtmp_record_publish;

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -15,15 +16,16 @@
#define NGX_RTMP_RECORD_OFF 0x01
#define NGX_RTMP_RECORD_AUDIO 0x02
#define NGX_RTMP_RECORD_VIDEO 0x04
#define NGX_RTMP_RECORD_KEYFRAMES 0x08
#define NGX_RTMP_RECORD_MANUAL 0x10
#define NGX_RTMP_RECORD_DATA 0x08
#define NGX_RTMP_RECORD_KEYFRAMES 0x10
#define NGX_RTMP_RECORD_MANUAL 0x20
typedef struct {
ngx_str_t id;
ngx_uint_t flags;
ngx_str_t path;
size_t max_size;
size_t interval_size;
size_t max_frames;
ngx_msec_t interval;
ngx_str_t suffix;
@ -50,6 +52,9 @@ typedef struct {
unsigned aac_header_sent:1;
unsigned avc_header_sent:1;
unsigned video_key_sent:1;
unsigned audio:1;
unsigned video:1;
unsigned record_started:1;
} ngx_rtmp_record_rec_ctx_t;
@ -68,7 +73,7 @@ ngx_uint_t ngx_rtmp_record_find(ngx_rtmp_record_app_conf_t *racf,
* 'n' is record node index in config array.
* Note: these functions allocate path in static buffer */
ngx_int_t ngx_rtmp_record_open(ngx_rtmp_session_t *s, ngx_uint_t n,
ngx_int_t ngx_rtmp_record_open(ngx_rtmp_session_t *s, ngx_uint_t n,
ngx_str_t *path);
ngx_int_t ngx_rtmp_record_close(ngx_rtmp_session_t *s, ngx_uint_t n,
ngx_str_t *path);
@ -80,10 +85,23 @@ typedef struct {
} ngx_rtmp_record_done_t;
typedef ngx_int_t (*ngx_rtmp_record_done_pt)(ngx_rtmp_session_t *s,
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

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -7,6 +8,7 @@
#include <ngx_core.h>
#include "ngx_rtmp_relay_module.h"
#include "ngx_rtmp_cmd_module.h"
#include "ngx_rtmp_codec_module.h"
static ngx_rtmp_publish_pt next_publish;
@ -18,11 +20,11 @@ static ngx_rtmp_close_stream_pt next_close_stream;
static ngx_int_t ngx_rtmp_relay_init_process(ngx_cycle_t *cycle);
static ngx_int_t ngx_rtmp_relay_postconfiguration(ngx_conf_t *cf);
static void * ngx_rtmp_relay_create_app_conf(ngx_conf_t *cf);
static char * ngx_rtmp_relay_merge_app_conf(ngx_conf_t *cf,
static char * ngx_rtmp_relay_merge_app_conf(ngx_conf_t *cf,
void *parent, void *child);
static char * ngx_rtmp_relay_push_pull(ngx_conf_t *cf, ngx_command_t *cmd,
static char * ngx_rtmp_relay_push_pull(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_int_t ngx_rtmp_relay_publish(ngx_rtmp_session_t *s,
static ngx_int_t ngx_rtmp_relay_publish(ngx_rtmp_session_t *s,
ngx_rtmp_publish_t *v);
static ngx_rtmp_relay_ctx_t * ngx_rtmp_relay_create_connection(
ngx_rtmp_conf_ctx_t *cctx, ngx_str_t* name,
@ -106,7 +108,7 @@ static ngx_command_t ngx_rtmp_relay_commands[] = {
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_rtmp_relay_app_conf_t, push_reconnect),
NULL },
{ ngx_string("pull_reconnect"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
@ -201,14 +203,14 @@ ngx_rtmp_relay_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_rtmp_relay_app_conf_t *prev = parent;
ngx_rtmp_relay_app_conf_t *conf = child;
conf->ctx = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_relay_ctx_t *)
conf->ctx = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_relay_ctx_t *)
* conf->nbuckets);
ngx_conf_merge_value(conf->session_relay, prev->session_relay, 0);
ngx_conf_merge_msec_value(conf->buflen, prev->buflen, 5000);
ngx_conf_merge_msec_value(conf->push_reconnect, prev->push_reconnect,
ngx_conf_merge_msec_value(conf->push_reconnect, prev->push_reconnect,
3000);
ngx_conf_merge_msec_value(conf->pull_reconnect, prev->pull_reconnect,
ngx_conf_merge_msec_value(conf->pull_reconnect, prev->pull_reconnect,
3000);
return NGX_CONF_OK;
@ -250,9 +252,9 @@ ngx_rtmp_relay_push_reconnect(ngx_event_t *ev)
ngx_uint_t n;
ngx_rtmp_relay_target_t *target, **t;
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"relay: push reconnect");
racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_relay_module);
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module);
@ -272,7 +274,7 @@ ngx_rtmp_relay_push_reconnect(ngx_event_t *ev)
for (pctx = ctx->play; pctx; pctx = pctx->next) {
if (pctx->tag == &ngx_rtmp_relay_module &&
pctx->data == target)
pctx->data == target)
{
break;
}
@ -289,7 +291,7 @@ ngx_rtmp_relay_push_reconnect(ngx_event_t *ev)
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"relay: push reconnect failed name='%V' app='%V' "
"playpath='%V' url='%V'",
&ctx->name, &target->app, &target->play_path,
&ctx->name, &target->app, &target->play_path,
&target->url.url);
if (!ctx->push_evt.timer_set) {
@ -299,14 +301,14 @@ ngx_rtmp_relay_push_reconnect(ngx_event_t *ev)
}
static ngx_int_t
static ngx_int_t
ngx_rtmp_relay_get_peer(ngx_peer_connection_t *pc, void *data)
{
return NGX_OK;
}
static void
static void
ngx_rtmp_relay_free_peer(ngx_peer_connection_t *pc, void *data,
ngx_uint_t state)
{
@ -352,7 +354,7 @@ ngx_rtmp_relay_create_connection(ngx_rtmp_conf_ctx_t *cctx, ngx_str_t* name,
racf = ngx_rtmp_get_module_app_conf(cctx, ngx_rtmp_relay_module);
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, racf->log, 0,
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, racf->log, 0,
"relay: create remote context");
pool = NULL;
@ -428,8 +430,8 @@ ngx_rtmp_relay_create_connection(ngx_rtmp_conf_ctx_t *cctx, ngx_str_t* name,
if (rctx->play_path.len == 0 && p != last) {
v.data = p;
v.len = last - p;
if (ngx_rtmp_relay_copy_str(pool, &rctx->play_path, &v)
!= NGX_OK)
if (ngx_rtmp_relay_copy_str(pool, &rctx->play_path, &v)
!= NGX_OK)
{
goto clear;
}
@ -467,7 +469,7 @@ ngx_rtmp_relay_create_connection(ngx_rtmp_conf_ctx_t *cctx, ngx_str_t* name,
rc = ngx_event_connect_peer(pc);
if (rc != NGX_OK && rc != NGX_AGAIN ) {
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, racf->log, 0,
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, racf->log, 0,
"relay: connection failed");
goto clear;
}
@ -495,10 +497,12 @@ ngx_rtmp_relay_create_connection(ngx_rtmp_conf_ctx_t *cctx, ngx_str_t* name,
}
rs->app_conf = cctx->app_conf;
rs->relay = 1;
rs->ready_for_publish = 0;
rctx->session = rs;
ngx_rtmp_set_ctx(rs, rctx, ngx_rtmp_relay_module);
ngx_str_set(&rs->flashver, "ngx-local-relay");
ngx_memcpy(&rs->app, &rctx->app, sizeof(rctx->app));
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_active, 1);
#endif
@ -534,7 +538,7 @@ ngx_rtmp_relay_create_local_ctx(ngx_rtmp_session_t *s, ngx_str_t *name,
{
ngx_rtmp_relay_ctx_t *ctx;
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"relay: create local context");
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module);
@ -555,8 +559,8 @@ ngx_rtmp_relay_create_local_ctx(ngx_rtmp_session_t *s, ngx_str_t *name,
return NULL;
}
if (ngx_rtmp_relay_copy_str(s->connection->pool, &ctx->name, name)
!= NGX_OK)
if (ngx_rtmp_relay_copy_str(s->connection->pool, &ctx->name, name)
!= NGX_OK)
{
return NULL;
}
@ -590,7 +594,7 @@ ngx_rtmp_relay_create(ngx_rtmp_session_t *s, ngx_str_t *name,
cctx = &racf->ctx[hash % racf->nbuckets];
for (; *cctx; cctx = &(*cctx)->next) {
if ((*cctx)->name.len == name->len
&& !ngx_memcmp(name->data, (*cctx)->name.data,
&& !ngx_memcmp(name->data, (*cctx)->name.data,
name->len))
{
break;
@ -623,7 +627,7 @@ ngx_int_t
ngx_rtmp_relay_pull(ngx_rtmp_session_t *s, ngx_str_t *name,
ngx_rtmp_relay_target_t *target)
{
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"relay: create pull name='%V' app='%V' playpath='%V' url='%V'",
name, &target->app, &target->play_path, &target->url.url);
@ -637,7 +641,7 @@ ngx_int_t
ngx_rtmp_relay_push(ngx_rtmp_session_t *s, ngx_str_t *name,
ngx_rtmp_relay_target_t *target)
{
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"relay: create push name='%V' app='%V' playpath='%V' url='%V'",
name, &target->app, &target->play_path, &target->url.url);
@ -690,7 +694,7 @@ ngx_rtmp_relay_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"relay: push failed name='%V' app='%V' "
"playpath='%V' url='%V'",
&name, &target->app, &target->play_path,
&name, &target->app, &target->play_path,
&target->url.url);
if (!ctx->push_evt.timer_set) {
@ -706,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;
@ -742,11 +749,14 @@ ngx_rtmp_relay_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"relay: pull failed name='%V' app='%V' "
"playpath='%V' url='%V'",
&name, &target->app, &target->play_path,
&name, &target->app, &target->play_path,
&target->url.url);
}
next:
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
"relay: ngx_rtmp_relay_play: next");
return next_play(s, v);
}
@ -764,7 +774,7 @@ ngx_rtmp_relay_play_local(ngx_rtmp_session_t *s)
ngx_memzero(&v, sizeof(ngx_rtmp_play_t));
v.silent = 1;
*(ngx_cpymem(v.name, ctx->name.data,
*(ngx_cpymem(v.name, ctx->name.data,
ngx_min(sizeof(v.name) - 1, ctx->name.len))) = 0;
return ngx_rtmp_play(s, &v);
@ -784,7 +794,7 @@ ngx_rtmp_relay_publish_local(ngx_rtmp_session_t *s)
ngx_memzero(&v, sizeof(ngx_rtmp_publish_t));
v.silent = 1;
*(ngx_cpymem(v.name, ctx->name.data,
*(ngx_cpymem(v.name, ctx->name.data,
ngx_min(sizeof(v.name) - 1, ctx->name.len))) = 0;
return ngx_rtmp_publish(s, &v);
@ -800,23 +810,23 @@ ngx_rtmp_relay_send_connect(ngx_rtmp_session_t *s)
static ngx_rtmp_amf_elt_t out_cmd[] = {
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_string("app"),
NULL, 0 }, /* <-- fill */
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_string("tcUrl"),
NULL, 0 }, /* <-- fill */
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_string("pageUrl"),
NULL, 0 }, /* <-- fill */
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_string("swfUrl"),
NULL, 0 }, /* <-- fill */
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_string("flashVer"),
NULL, 0 }, /* <-- fill */
@ -832,7 +842,7 @@ ngx_rtmp_relay_send_connect(ngx_rtmp_session_t *s)
static ngx_rtmp_amf_elt_t out_elts[] = {
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
ngx_null_string,
"connect", 0 },
{ NGX_RTMP_AMF_NUMBER,
@ -873,7 +883,7 @@ ngx_rtmp_relay_send_connect(ngx_rtmp_session_t *s)
out_cmd[1].data = ctx->tc_url.data;
out_cmd[1].len = ctx->tc_url.len;
} else {
len = sizeof("rtmp://") - 1 + ctx->url.len +
len = sizeof("rtmp://") - 1 + ctx->url.len +
sizeof("/") - 1 + ctx->app.len;
p = ngx_palloc(s->connection->pool, len);
if (p == NULL) {
@ -920,7 +930,7 @@ ngx_rtmp_relay_send_connect(ngx_rtmp_session_t *s)
|| ngx_rtmp_send_amf(s, &h, out_elts,
sizeof(out_elts) / sizeof(out_elts[0])) != NGX_OK
? NGX_ERROR
: NGX_OK;
: NGX_OK;
}
@ -932,7 +942,7 @@ ngx_rtmp_relay_send_create_stream(ngx_rtmp_session_t *s)
static ngx_rtmp_amf_elt_t out_elts[] = {
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
ngx_null_string,
"createStream", 0 },
{ NGX_RTMP_AMF_NUMBER,
@ -964,7 +974,7 @@ ngx_rtmp_relay_send_publish(ngx_rtmp_session_t *s)
static ngx_rtmp_amf_elt_t out_elts[] = {
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
ngx_null_string,
"publish", 0 },
{ NGX_RTMP_AMF_NUMBER,
@ -1020,7 +1030,7 @@ ngx_rtmp_relay_send_play(ngx_rtmp_session_t *s)
static ngx_rtmp_amf_elt_t out_elts[] = {
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
ngx_null_string,
"play", 0 },
{ NGX_RTMP_AMF_NUMBER,
@ -1078,7 +1088,7 @@ ngx_rtmp_relay_send_play(ngx_rtmp_session_t *s)
return ngx_rtmp_send_amf(s, &h, out_elts,
sizeof(out_elts) / sizeof(out_elts[0])) != NGX_OK
|| ngx_rtmp_send_set_buflen(s, NGX_RTMP_RELAY_MSID,
|| ngx_rtmp_send_set_buflen(s, NGX_RTMP_RELAY_MSID,
racf->buflen) != NGX_OK
? NGX_ERROR
: NGX_OK;
@ -1099,11 +1109,11 @@ ngx_rtmp_relay_on_result(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
static ngx_rtmp_amf_elt_t in_inf[] = {
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_string("level"),
&v.level, sizeof(v.level) },
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_string("code"),
&v.code, sizeof(v.code) },
@ -1134,13 +1144,13 @@ ngx_rtmp_relay_on_result(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
}
ngx_memzero(&v, sizeof(v));
if (ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
if (ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
{
return NGX_ERROR;
}
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"relay: _result: level='%s' code='%s' description='%s'",
v.level, v.code, v.desc);
@ -1182,11 +1192,11 @@ ngx_rtmp_relay_on_error(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
static ngx_rtmp_amf_elt_t in_inf[] = {
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_string("level"),
&v.level, sizeof(v.level) },
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_string("code"),
&v.code, sizeof(v.code) },
@ -1217,25 +1227,173 @@ ngx_rtmp_relay_on_error(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
}
ngx_memzero(&v, sizeof(v));
if (ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
if (ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
{
return NGX_ERROR;
}
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"relay: _error: level='%s' code='%s' description='%s'",
v.level, v.code, v.desc);
return NGX_OK;
}
static ngx_int_t
ngx_rtmp_relay_send_set_data_frame(ngx_rtmp_session_t *s)
{
ngx_rtmp_relay_ctx_t *ctx;
ngx_rtmp_codec_ctx_t *codec_ctx;
ngx_rtmp_header_t hdr;
static struct {
double width;
double height;
double duration;
double frame_rate;
double video_data_rate;
double video_codec_id;
double audio_data_rate;
double audio_codec_id;
u_char profile[32];
u_char level[32];
} v;
static ngx_rtmp_amf_elt_t out_inf[] = {
{ NGX_RTMP_AMF_STRING,
ngx_string("Server"),
"NGINX RTMP (github.com/arut/nginx-rtmp-module)", 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("width"),
&v.width, 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("height"),
&v.height, 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("displayWidth"),
&v.width, 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("displayHeight"),
&v.height, 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("duration"),
&v.duration, 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("framerate"),
&v.frame_rate, 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("fps"),
&v.frame_rate, 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("videodatarate"),
&v.video_data_rate, 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("videocodecid"),
&v.video_codec_id, 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("audiodatarate"),
&v.audio_data_rate, 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("audiocodecid"),
&v.audio_codec_id, 0 },
{ NGX_RTMP_AMF_STRING,
ngx_string("profile"),
&v.profile, sizeof(v.profile) },
{ NGX_RTMP_AMF_STRING,
ngx_string("level"),
&v.level, sizeof(v.level) }
};
static ngx_rtmp_amf_elt_t out_elts[] = {
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
"@setDataFrame", 0 },
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
"onMetaData", 0 },
{ NGX_RTMP_AMF_OBJECT,
ngx_null_string,
out_inf, sizeof(out_inf) }
};
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module);
if (ctx == NULL || !s->relay) {
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"relay: couldn't get relay context");
return NGX_OK;
}
/* we need to get the codec context from the incoming publisher in order to
* send the metadata along */
codec_ctx = ngx_rtmp_get_module_ctx(ctx->publish->session,
ngx_rtmp_codec_module);
if (codec_ctx == NULL) {
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"relay: couldn't get codec context");
return NGX_OK;
}
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"relay: data frame from codec context: "
"width=%ui height=%ui duration=%ui frame_rate=%ui "
"video_codec_id=%ui audio_codec_id=%ui",
codec_ctx->width, codec_ctx->height, codec_ctx->duration,
codec_ctx->frame_rate, codec_ctx->video_codec_id,
codec_ctx->audio_codec_id);
/* we only want to send the metadata if the codec module has already
* parsed it -- is there a better way to check this? */
if (codec_ctx->width > 0 && codec_ctx->height > 0) {
v.width = codec_ctx->width;
v.height = codec_ctx->height;
v.duration = codec_ctx->duration;
v.frame_rate = codec_ctx->frame_rate;
v.video_data_rate = codec_ctx->video_data_rate;
v.video_codec_id = codec_ctx->video_codec_id;
v.audio_data_rate = codec_ctx->audio_data_rate;
v.audio_codec_id = codec_ctx->audio_codec_id;
ngx_memcpy(v.profile, codec_ctx->profile, sizeof(codec_ctx->profile));
ngx_memcpy(v.level, codec_ctx->level, sizeof(codec_ctx->level));
ngx_memzero(&hdr, sizeof(hdr));
hdr.csid = NGX_RTMP_RELAY_CSID_AMF_INI;
hdr.msid = NGX_RTMP_RELAY_MSID;
hdr.type = NGX_RTMP_MSG_AMF_META;
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"relay: sending @setDataFrame");
return ngx_rtmp_send_amf(s, &hdr, out_elts,
sizeof(out_elts) / sizeof(out_elts[0]));
}
return NGX_OK;
}
static ngx_int_t
ngx_rtmp_relay_on_status(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
ngx_rtmp_relay_ctx_t *ctx;
static struct {
double trans;
u_char level[32];
@ -1245,11 +1403,11 @@ ngx_rtmp_relay_on_status(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
static ngx_rtmp_amf_elt_t in_inf[] = {
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_string("level"),
&v.level, sizeof(v.level) },
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_string("code"),
&v.code, sizeof(v.code) },
@ -1288,20 +1446,68 @@ ngx_rtmp_relay_on_status(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_memzero(&v, sizeof(v));
if (h->type == NGX_RTMP_MSG_AMF_META) {
ngx_rtmp_receive_amf(s, in, in_elts_meta,
ngx_rtmp_receive_amf(s, in, in_elts_meta,
sizeof(in_elts_meta) / sizeof(in_elts_meta[0]));
} else {
ngx_rtmp_receive_amf(s, in, in_elts,
ngx_rtmp_receive_amf(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0]));
}
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"relay: onStatus: level='%s' code='%s' description='%s'",
v.level, v.code, v.desc);
/* when doing a push to Adobe Media Server, we have to use the
* @setDataFrame command to send the metadata
* see: http://help.adobe.com/en_US/adobemediaserver/devguide/WS5b3ccc516d4fbf351e63e3d11a0773d56e-7ff6Dev.2.3.html
*/
if (!ngx_strncasecmp(v.code, (u_char *)"NetStream.Publish.Start",
ngx_strlen("NetStream.Publish.Start"))) {
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"relay: sending metadata from NetStream.Publish.Start from player");
s->ready_for_publish = 1;
if (ngx_rtmp_relay_send_set_data_frame(s) != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"relay: unable to send metadata via @setDataFrame");
}
}
return NGX_OK;
}
static ngx_int_t
ngx_rtmp_relay_on_meta_data(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
/* when we receive onMetaData, the session (s) is our incoming publisher's
* session, so we need to send the @setDataFrame to our ctx->play->session */
ngx_rtmp_relay_ctx_t *ctx;
ngx_rtmp_relay_ctx_t *pctx;
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"relay: got metadata from @setDataFrame invocation from publisher.");
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module);
if (ctx == NULL) {
return NGX_OK;
}
for (pctx = ctx->play; pctx; pctx = pctx->next) {
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"relay: %ssending metadata from @setDataFrame invocation from publisher to %V/%V/%V",
(pctx->session->relay && pctx->session->ready_for_publish) ? "" : "not ", &pctx->url, &pctx->app, &pctx->play_path);
if (!pctx->session->relay || !pctx->session->ready_for_publish) continue;
if (ngx_rtmp_relay_send_set_data_frame(pctx->session) != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"relay: unable to send @setDataFrame to %V/%V", &pctx->url, &pctx->play_path);
}
}
return NGX_OK;
}
static ngx_int_t
ngx_rtmp_relay_handshake_done(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
@ -1334,7 +1540,6 @@ ngx_rtmp_relay_close(ngx_rtmp_session_t *s)
if (s->static_relay) {
ngx_add_timer(ctx->static_evt, racf->pull_reconnect);
return;
}
if (ctx->publish == NULL) {
@ -1350,13 +1555,13 @@ ngx_rtmp_relay_close(ngx_rtmp_session_t *s)
}
}
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, ctx->session->connection->log, 0,
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, ctx->session->connection->log, 0,
"relay: play disconnect app='%V' name='%V'",
&ctx->app, &ctx->name);
/* push reconnect */
if (s->relay && ctx->tag == &ngx_rtmp_relay_module &&
!ctx->publish->push_evt.timer_set)
if (s->relay && ctx->tag == &ngx_rtmp_relay_module &&
!ctx->publish->push_evt.timer_set)
{
ngx_add_timer(&ctx->publish->push_evt, racf->push_reconnect);
}
@ -1372,8 +1577,8 @@ ngx_rtmp_relay_close(ngx_rtmp_session_t *s)
#endif
if (ctx->publish->play == NULL && ctx->publish->session->relay) {
ngx_log_debug2(NGX_LOG_DEBUG_RTMP,
ctx->publish->session->connection->log, 0,
ngx_log_debug2(NGX_LOG_DEBUG_RTMP,
ctx->publish->session->connection->log, 0,
"relay: publish disconnect empty app='%V' name='%V'",
&ctx->app, &ctx->name);
ngx_rtmp_finalize_session(ctx->publish->session);
@ -1385,7 +1590,7 @@ ngx_rtmp_relay_close(ngx_rtmp_session_t *s)
}
/* publish end disconnect */
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, ctx->session->connection->log, 0,
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, ctx->session->connection->log, 0,
"relay: publish disconnect app='%V' name='%V'",
&ctx->app, &ctx->name);
@ -1395,7 +1600,7 @@ ngx_rtmp_relay_close(ngx_rtmp_session_t *s)
for (cctx = &ctx->play; *cctx; cctx = &(*cctx)->next) {
(*cctx)->publish = NULL;
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, (*cctx)->session->connection->log,
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, (*cctx)->session->connection->log,
0, "relay: play disconnect orphan app='%V' name='%V'",
&(*cctx)->app, &(*cctx)->name);
ngx_rtmp_finalize_session((*cctx)->session);
@ -1471,7 +1676,7 @@ ngx_rtmp_relay_push_pull(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
u->url.data += 7;
u->url.len -= 7;
}
if (ngx_parse_url(cf->pool, u) != NGX_OK) {
if (u->err) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
@ -1621,7 +1826,7 @@ ngx_rtmp_relay_init_process(ngx_cycle_t *cycle)
pcscf = cmcf->servers.elts;
for (n = 0; n < cmcf->servers.nelts; ++n, ++pcscf) {
cscf = *pcscf;
pcacf = cscf->applications.elts;
@ -1686,5 +1891,9 @@ ngx_rtmp_relay_postconfiguration(ngx_conf_t *cf)
ngx_str_set(&ch->name, "onStatus");
ch->handler = ngx_rtmp_relay_on_status;
ch = ngx_array_push(&cmcf->amf);
ngx_str_set(&ch->name, "@setDataFrame");
ch->handler = ngx_rtmp_relay_on_meta_data;
return NGX_OK;
}

View file

@ -1,11 +1,12 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
#ifndef _NGX_RTMP_RELAY_H_INCLUDED_
#define _NGX_RTMP_RELAY_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>

View file

@ -1,7 +1,7 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
*/
/*
* Copyright (C) Roman Arutyunyan
*/
#include <ngx_config.h>
#include <ngx_core.h>
@ -25,7 +25,7 @@
if (__l == NULL) { \
return NULL; \
} \
__b = __l->buf;
__b = __l->buf;
#define NGX_RTMP_UCTL_START(s, type, utype) \
NGX_RTMP_USER_START(s, type); \
@ -99,7 +99,7 @@ ngx_rtmp_create_abort(ngx_rtmp_session_t *s, uint32_t csid)
"create: abort csid=%uD", csid);
{
NGX_RTMP_USER_START(s, NGX_RTMP_MSG_CHUNK_SIZE);
NGX_RTMP_USER_START(s, NGX_RTMP_MSG_ABORT);
NGX_RTMP_USER_OUT4(csid);
@ -169,7 +169,7 @@ ngx_rtmp_create_bandwidth(ngx_rtmp_session_t *s, uint32_t ack_size,
uint8_t limit_type)
{
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"create: bandwidth ack_size=%uD limit=%d",
"create: bandwidth ack_size=%uD limit=%d",
ack_size, (int)limit_type);
{
@ -223,7 +223,7 @@ ngx_rtmp_create_stream_eof(ngx_rtmp_session_t *s, uint32_t msid)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"create: stream_end msid=%uD", msid);
{
NGX_RTMP_UCTL_START(s, NGX_RTMP_MSG_USER, NGX_RTMP_USER_STREAM_EOF);
@ -267,7 +267,7 @@ ngx_rtmp_send_stream_dry(ngx_rtmp_session_t *s, uint32_t msid)
ngx_chain_t *
ngx_rtmp_create_set_buflen(ngx_rtmp_session_t *s, uint32_t msid,
ngx_rtmp_create_set_buflen(ngx_rtmp_session_t *s, uint32_t msid,
uint32_t buflen_msec)
{
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
@ -286,7 +286,7 @@ ngx_rtmp_create_set_buflen(ngx_rtmp_session_t *s, uint32_t msid,
ngx_int_t
ngx_rtmp_send_set_buflen(ngx_rtmp_session_t *s, uint32_t msid,
ngx_rtmp_send_set_buflen(ngx_rtmp_session_t *s, uint32_t msid,
uint32_t buflen_msec)
{
return ngx_rtmp_send_shared_packet(s,
@ -295,7 +295,7 @@ ngx_rtmp_send_set_buflen(ngx_rtmp_session_t *s, uint32_t msid,
ngx_chain_t *
ngx_rtmp_create_recorded(ngx_rtmp_session_t *s, uint32_t msid)
ngx_rtmp_create_recorded(ngx_rtmp_session_t *s, uint32_t msid)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"create: recorded msid=%uD", msid);
@ -311,7 +311,7 @@ ngx_rtmp_create_recorded(ngx_rtmp_session_t *s, uint32_t msid)
ngx_int_t
ngx_rtmp_send_recorded(ngx_rtmp_session_t *s, uint32_t msid)
ngx_rtmp_send_recorded(ngx_rtmp_session_t *s, uint32_t msid)
{
return ngx_rtmp_send_shared_packet(s,
ngx_rtmp_create_recorded(s, msid));
@ -319,7 +319,7 @@ ngx_rtmp_send_recorded(ngx_rtmp_session_t *s, uint32_t msid)
ngx_chain_t *
ngx_rtmp_create_ping_request(ngx_rtmp_session_t *s, uint32_t timestamp)
ngx_rtmp_create_ping_request(ngx_rtmp_session_t *s, uint32_t timestamp)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"create: ping_request timestamp=%uD", timestamp);
@ -335,7 +335,7 @@ ngx_rtmp_create_ping_request(ngx_rtmp_session_t *s, uint32_t timestamp)
ngx_int_t
ngx_rtmp_send_ping_request(ngx_rtmp_session_t *s, uint32_t timestamp)
ngx_rtmp_send_ping_request(ngx_rtmp_session_t *s, uint32_t timestamp)
{
return ngx_rtmp_send_shared_packet(s,
ngx_rtmp_create_ping_request(s, timestamp));
@ -343,7 +343,7 @@ ngx_rtmp_send_ping_request(ngx_rtmp_session_t *s, uint32_t timestamp)
ngx_chain_t *
ngx_rtmp_create_ping_response(ngx_rtmp_session_t *s, uint32_t timestamp)
ngx_rtmp_create_ping_response(ngx_rtmp_session_t *s, uint32_t timestamp)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"create: ping_response timestamp=%uD", timestamp);
@ -359,14 +359,14 @@ ngx_rtmp_create_ping_response(ngx_rtmp_session_t *s, uint32_t timestamp)
ngx_int_t
ngx_rtmp_send_ping_response(ngx_rtmp_session_t *s, uint32_t timestamp)
ngx_rtmp_send_ping_response(ngx_rtmp_session_t *s, uint32_t timestamp)
{
return ngx_rtmp_send_shared_packet(s,
ngx_rtmp_create_ping_response(s, timestamp));
}
static ngx_chain_t *
static ngx_chain_t *
ngx_rtmp_alloc_amf_buf(void *arg)
{
return ngx_rtmp_alloc_shared_buf((ngx_rtmp_core_srv_conf_t *)arg);
@ -378,7 +378,7 @@ ngx_rtmp_alloc_amf_buf(void *arg)
/* NOTE: this function does not free shared bufs on error */
ngx_int_t
ngx_rtmp_append_amf(ngx_rtmp_session_t *s,
ngx_chain_t **first, ngx_chain_t **last,
ngx_chain_t **first, ngx_chain_t **last,
ngx_rtmp_amf_elt_t *elts, size_t nelts)
{
ngx_rtmp_amf_ctx_t act;
@ -462,26 +462,26 @@ ngx_rtmp_create_status(ngx_rtmp_session_t *s, char *code, char* level,
static ngx_rtmp_amf_elt_t out_inf[] = {
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_string("level"),
NULL, 0 },
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_string("code"),
NULL, 0 },
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_string("description"),
NULL, 0 },
};
static ngx_rtmp_amf_elt_t out_elts[] = {
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
"onStatus", 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_null_string,
&trans, 0 },
@ -502,6 +502,7 @@ ngx_rtmp_create_status(ngx_rtmp_session_t *s, char *code, char* level,
out_inf[0].data = level;
out_inf[1].data = code;
out_inf[2].data = desc;
trans = 0;
memset(&h, 0, sizeof(h));
@ -509,7 +510,7 @@ ngx_rtmp_create_status(ngx_rtmp_session_t *s, char *code, char* level,
h.csid = NGX_RTMP_CSID_AMF;
h.msid = NGX_RTMP_MSID;
return ngx_rtmp_create_amf(s, &h, out_elts,
return ngx_rtmp_create_amf(s, &h, out_elts,
sizeof(out_elts) / sizeof(out_elts[0]));
}
@ -532,26 +533,26 @@ ngx_rtmp_create_play_status(ngx_rtmp_session_t *s, char *code, char* level,
static ngx_rtmp_amf_elt_t out_inf[] = {
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_string("code"),
NULL, 0 },
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_string("level"),
NULL, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("duration"),
&dduration, 0 },
{ NGX_RTMP_AMF_NUMBER,
{ NGX_RTMP_AMF_NUMBER,
ngx_string("bytes"),
&dbytes, 0 },
};
static ngx_rtmp_amf_elt_t out_elts[] = {
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
"onPlayStatus", 0 },
@ -579,7 +580,7 @@ ngx_rtmp_create_play_status(ngx_rtmp_session_t *s, char *code, char* level,
h.msid = NGX_RTMP_MSID;
h.timestamp = duration;
return ngx_rtmp_create_amf(s, &h, out_elts,
return ngx_rtmp_create_amf(s, &h, out_elts,
sizeof(out_elts) / sizeof(out_elts[0]));
}
@ -593,6 +594,358 @@ ngx_rtmp_send_play_status(ngx_rtmp_session_t *s, char *code, char* level,
}
// ----------- Based on Adobe FMS 3 application.redirectConnection description --------- //
ngx_chain_t *
ngx_rtmp_create_redirect_status(ngx_rtmp_session_t *s, char *callMethod, char *desc, ngx_str_t to_url)
{
ngx_rtmp_header_t h;
static double dtrans;
static double dcode;
ngx_log_debug0(NGX_LOG_DEBUG, s->connection->log, 0,
"create redirect status: got data");
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);
static ngx_rtmp_amf_elt_t out_inf_ex_data[] = {
{ NGX_RTMP_AMF_NUMBER,
ngx_string("code"),
&dcode, 0 },
{ NGX_RTMP_AMF_STRING,
ngx_string("redirect"),
NULL, 0 },
};
static ngx_rtmp_amf_elt_t out_inf[] = {
{ NGX_RTMP_AMF_STRING,
ngx_string("level"),
"error", 0 },
{ NGX_RTMP_AMF_STRING,
ngx_string("code"),
"NetConnection.Connect.Rejected", 0 },
{ NGX_RTMP_AMF_STRING,
ngx_string("description"),
NULL, 0 },
{ NGX_RTMP_AMF_OBJECT,
ngx_string("ex"),
out_inf_ex_data,
sizeof(out_inf_ex_data) },
};
static ngx_rtmp_amf_elt_t out_elts[] = {
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
NULL, 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_null_string,
&dtrans, 0 },
{ NGX_RTMP_AMF_NULL,
ngx_null_string,
NULL, 0 },
{ NGX_RTMP_AMF_OBJECT,
ngx_null_string,
out_inf,
sizeof(out_inf) },
};
ngx_log_debug0(NGX_LOG_DEBUG, s->connection->log, 0,
"create redirect status: set structure data");
out_elts[0].data = callMethod;
out_inf[2].data = desc;
dcode = 302;
dtrans = 0;
out_inf_ex_data[1].data = to_url.data;
ngx_memzero(&h, sizeof(h));
h.type = NGX_RTMP_MSG_AMF_CMD;
h.csid = NGX_RTMP_CSID_AMF;
h.msid = NGX_RTMP_MSID;
return ngx_rtmp_create_amf(s, &h, out_elts,
sizeof(out_elts) / sizeof(out_elts[0]));
}
ngx_int_t
ngx_rtmp_send_redirect_status(ngx_rtmp_session_t *s,
char *callMethod, char *desc, ngx_str_t to_url)
{
return ngx_rtmp_send_shared_packet(s,
ngx_rtmp_create_redirect_status(s, callMethod, desc, to_url));
}
ngx_chain_t *
ngx_rtmp_create_close_method(ngx_rtmp_session_t *s, char *methodName)
{
ngx_rtmp_header_t h;
static double dtrans;
static ngx_rtmp_amf_elt_t out_elts[] = {
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
NULL, 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_null_string,
&dtrans, 0 },
};
ngx_log_debug0(NGX_LOG_DEBUG, s->connection->log, 0,
"create close method: set structure data");
out_elts[0].data = methodName;
dtrans = 0;
ngx_memzero(&h, sizeof(h));
h.type = NGX_RTMP_MSG_AMF_CMD;
h.csid = NGX_RTMP_CSID_AMF;
h.msid = NGX_RTMP_MSID;
return ngx_rtmp_create_amf(s, &h, out_elts,
sizeof(out_elts) / sizeof(out_elts[0]));
}
ngx_int_t
ngx_rtmp_send_close_method(ngx_rtmp_session_t *s, char *methodName)
{
return ngx_rtmp_send_shared_packet(s,
ngx_rtmp_create_close_method(s, methodName));
}
ngx_chain_t *
ngx_rtmp_create_fcpublish(ngx_rtmp_session_t *s, u_char *desc)
{
ngx_rtmp_header_t h;
static double trans;
static ngx_rtmp_amf_elt_t out_inf[] = {
{ NGX_RTMP_AMF_STRING,
ngx_string("level"),
"status", 0 },
{ NGX_RTMP_AMF_STRING,
ngx_string("code"),
"NetStream.Publish.Start", 0 },
{ NGX_RTMP_AMF_STRING,
ngx_string("description"),
NULL, 0 },
};
static ngx_rtmp_amf_elt_t out_elts[] = {
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
"onFCPublish", 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_null_string,
&trans, 0 },
{ NGX_RTMP_AMF_NULL,
ngx_null_string,
NULL, 0 },
{ NGX_RTMP_AMF_OBJECT,
ngx_null_string,
out_inf,
sizeof(out_inf) },
};
ngx_log_debug0(NGX_LOG_DEBUG, s->connection->log, 0,
"create: fcpublish - set structure data");
out_inf[2].data = desc;
// trans = 3.0; // magick from ffmpeg
trans = 0;
memset(&h, 0, sizeof(h));
h.type = NGX_RTMP_MSG_AMF_CMD;
h.csid = NGX_RTMP_CSID_AMF;
h.msid = NGX_RTMP_MSID;
return ngx_rtmp_create_amf(s, &h, out_elts,
sizeof(out_elts) / sizeof(out_elts[0]));
}
ngx_int_t
ngx_rtmp_send_fcpublish(ngx_rtmp_session_t *s, u_char *desc)
{
return ngx_rtmp_send_shared_packet(s,
ngx_rtmp_create_fcpublish(s, desc));
}
ngx_chain_t *
ngx_rtmp_create_fcunpublish(ngx_rtmp_session_t *s, u_char *desc)
{
ngx_rtmp_header_t h;
static double trans;
static ngx_rtmp_amf_elt_t out_inf[] = {
{ NGX_RTMP_AMF_STRING,
ngx_string("level"),
"status", 0 },
{ NGX_RTMP_AMF_STRING,
ngx_string("code"),
"NetStream.Unpublish.Success", 0 },
{ NGX_RTMP_AMF_STRING,
ngx_string("description"),
NULL, 0 },
};
static ngx_rtmp_amf_elt_t out_elts[] = {
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
"onFCUnpublish", 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_null_string,
&trans, 0 },
{ NGX_RTMP_AMF_NULL,
ngx_null_string,
NULL, 0 },
{ NGX_RTMP_AMF_OBJECT,
ngx_null_string,
out_inf,
sizeof(out_inf) },
};
ngx_log_debug0(NGX_LOG_DEBUG, s->connection->log, 0,
"create: fcunpublish - set structure data");
out_inf[2].data = desc;
// trans = 5.0; // magick from ffmpeg
trans = 0;
memset(&h, 0, sizeof(h));
h.type = NGX_RTMP_MSG_AMF_CMD;
h.csid = NGX_RTMP_CSID_AMF;
h.msid = NGX_RTMP_MSID;
return ngx_rtmp_create_amf(s, &h, out_elts,
sizeof(out_elts) / sizeof(out_elts[0]));
}
ngx_int_t
ngx_rtmp_send_fcunpublish(ngx_rtmp_session_t *s, u_char *desc)
{
return ngx_rtmp_send_shared_packet(s,
ngx_rtmp_create_fcunpublish(s, desc));
}
ngx_chain_t *
ngx_rtmp_create_fi(ngx_rtmp_session_t *s)
{
ngx_rtmp_header_t h;
static double trans;
struct tm tm;
struct timeval tv;
static u_char buf_time[NGX_TIME_T_LEN*2 + 1];
static u_char buf_date[NGX_TIME_T_LEN + 1];
static ngx_rtmp_amf_elt_t out_inf[] = {
{ NGX_RTMP_AMF_STRING,
ngx_string("st"),
NULL, 0 },
{ NGX_RTMP_AMF_STRING,
ngx_string("sd"),
NULL, 0 },
};
static ngx_rtmp_amf_elt_t out_elts[] = {
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
"onFI", 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_null_string,
&trans, 0 },
{ NGX_RTMP_AMF_NULL,
ngx_null_string,
NULL, 0 },
{ NGX_RTMP_AMF_OBJECT,
ngx_null_string,
out_inf,
sizeof(out_inf) },
};
trans = 0;
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));
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
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;
memset(&h, 0, sizeof(h));
h.type = NGX_RTMP_MSG_AMF_CMD;
h.csid = NGX_RTMP_CSID_AMF;
h.msid = NGX_RTMP_MSID;
return ngx_rtmp_create_amf(s, &h, out_elts,
sizeof(out_elts) / sizeof(out_elts[0]));
}
ngx_int_t
ngx_rtmp_send_fi(ngx_rtmp_session_t *s)
{
return ngx_rtmp_send_shared_packet(s,
ngx_rtmp_create_fi(s));
}
ngx_chain_t *
ngx_rtmp_create_sample_access(ngx_rtmp_session_t *s)
{
@ -602,7 +955,7 @@ ngx_rtmp_create_sample_access(ngx_rtmp_session_t *s)
static ngx_rtmp_amf_elt_t access_elts[] = {
{ NGX_RTMP_AMF_STRING,
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
"|RtmpSampleAccess", 0 },

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
@ -8,7 +9,7 @@
#include "ngx_rtmp.h"
ngx_chain_t *
ngx_chain_t *
ngx_rtmp_alloc_shared_buf(ngx_rtmp_core_srv_conf_t *cscf)
{
u_char *p;
@ -24,7 +25,7 @@ ngx_rtmp_alloc_shared_buf(ngx_rtmp_core_srv_conf_t *cscf)
size = cscf->chunk_size + NGX_RTMP_MAX_CHUNK_HEADER;
p = ngx_pcalloc(cscf->pool, NGX_RTMP_REFCOUNT_BYTES
p = ngx_pcalloc(cscf->pool, NGX_RTMP_REFCOUNT_BYTES
+ sizeof(ngx_chain_t)
+ sizeof(ngx_buf_t)
+ size);
@ -55,7 +56,7 @@ ngx_rtmp_alloc_shared_buf(ngx_rtmp_core_srv_conf_t *cscf)
}
void
void
ngx_rtmp_free_shared_chain(ngx_rtmp_core_srv_conf_t *cscf, ngx_chain_t *in)
{
ngx_chain_t *cl;
@ -65,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;
@ -75,7 +77,7 @@ ngx_rtmp_free_shared_chain(ngx_rtmp_core_srv_conf_t *cscf, ngx_chain_t *in)
ngx_chain_t *
ngx_rtmp_append_shared_bufs(ngx_rtmp_core_srv_conf_t *cscf,
ngx_rtmp_append_shared_bufs(ngx_rtmp_core_srv_conf_t *cscf,
ngx_chain_t *head, ngx_chain_t *in)
{
ngx_chain_t *l, **ll;
@ -104,7 +106,7 @@ ngx_rtmp_append_shared_bufs(ngx_rtmp_core_srv_conf_t *cscf,
}
while (l->buf->end - l->buf->last >= in->buf->last - p) {
l->buf->last = ngx_cpymem(l->buf->last, p,
l->buf->last = ngx_cpymem(l->buf->last, p,
in->buf->last - p);
in = in->next;
if (in == NULL) {

View file

@ -1,22 +1,26 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include <nginx.h>
#include "ngx_rtmp.h"
#include "ngx_rtmp_version.h"
#include "ngx_rtmp_live_module.h"
#include "ngx_rtmp_play_module.h"
#include "ngx_rtmp_codec_module.h"
#include "ngx_rtmp_record_module.h"
static ngx_int_t ngx_rtmp_stat_init_process(ngx_cycle_t *cycle);
static char *ngx_rtmp_stat(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_int_t ngx_rtmp_stat_postconfiguration(ngx_conf_t *cf);
static void * ngx_rtmp_stat_create_loc_conf(ngx_conf_t *cf);
static char * ngx_rtmp_stat_merge_loc_conf(ngx_conf_t *cf,
static char * ngx_rtmp_stat_merge_loc_conf(ngx_conf_t *cf,
void *parent, void *child);
@ -46,7 +50,7 @@ static ngx_conf_bitmask_t ngx_rtmp_stat_masks[] = {
{ ngx_string("live"), NGX_RTMP_STAT_LIVE },
{ ngx_string("clients"), NGX_RTMP_STAT_CLIENTS },
{ ngx_null_string, 0 }
};
};
static ngx_command_t ngx_rtmp_stat_commands[] = {
@ -70,33 +74,33 @@ static ngx_command_t ngx_rtmp_stat_commands[] = {
static ngx_http_module_t ngx_rtmp_stat_module_ctx = {
NULL, /* preconfiguration */
ngx_rtmp_stat_postconfiguration, /* postconfiguration */
NULL, /* preconfiguration */
ngx_rtmp_stat_postconfiguration, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_rtmp_stat_create_loc_conf, /* create location configuration */
ngx_rtmp_stat_merge_loc_conf, /* merge location configuration */
ngx_rtmp_stat_create_loc_conf, /* create location configuration */
ngx_rtmp_stat_merge_loc_conf, /* merge location configuration */
};
ngx_module_t ngx_rtmp_stat_module = {
NGX_MODULE_V1,
&ngx_rtmp_stat_module_ctx, /* module context */
ngx_rtmp_stat_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
ngx_rtmp_stat_init_process, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
NGX_MODULE_V1,
&ngx_rtmp_stat_module_ctx, /* module context */
ngx_rtmp_stat_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
ngx_rtmp_stat_init_process, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
@ -106,13 +110,13 @@ ngx_module_t ngx_rtmp_stat_module = {
static ngx_int_t
ngx_rtmp_stat_init_process(ngx_cycle_t *cycle)
{
/*
/*
* HTTP process initializer is called
* after event module initializer
* so we can run posted events here
*/
ngx_event_process_posted(cycle, &ngx_rtmp_init_queue);
ngx_event_process_posted(cycle, (ngx_queue_t*) &ngx_rtmp_init_queue);
return NGX_OK;
}
@ -156,7 +160,7 @@ ngx_rtmp_stat_escape(ngx_http_request_t *r, void *data, size_t len)
}
#if (NGX_WIN32)
/*
/*
* Fix broken MSVC memcpy optimization for 4-byte data
* when this function is inlined
*/
@ -196,7 +200,7 @@ ngx_rtmp_stat_output(ngx_http_request_t *r, ngx_chain_t ***lll,
if (cl == NULL) {
return;
}
b = ngx_create_temp_buf(r->pool,
b = ngx_create_temp_buf(r->pool,
ngx_max(NGX_RTMP_STAT_BUFSIZE, real_len));
if (b == NULL || b->pos == NULL) {
return;
@ -242,34 +246,39 @@ ngx_rtmp_stat_output(ngx_http_request_t *r, ngx_chain_t ***lll,
#define NGX_RTMP_STAT_ECS(s) NGX_RTMP_STAT_E((s), ngx_strlen(s))
#define NGX_RTMP_STAT_BW 0x01
#define NGX_RTMP_STAT_BYTES 0x02
#define NGX_RTMP_STAT_BW_BYTES 0x03
static void
ngx_rtmp_stat_bw(ngx_http_request_t *r, ngx_chain_t ***lll,
ngx_rtmp_bandwidth_t *bw_in, ngx_rtmp_bandwidth_t *bw_out)
ngx_rtmp_bandwidth_t *bw, char *name,
ngx_uint_t flags)
{
u_char buf[NGX_INT64_LEN + 1];
u_char buf[NGX_INT64_LEN + 9];
ngx_rtmp_update_bandwidth(bw_in, 0);
ngx_rtmp_update_bandwidth(bw_out, 0);
ngx_rtmp_update_bandwidth(bw, 0);
NGX_RTMP_STAT_L("<in>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%uL", bw_in->bytes) - buf);
NGX_RTMP_STAT_L("</in>\r\n");
if (flags & NGX_RTMP_STAT_BW) {
NGX_RTMP_STAT_L("<bw_");
NGX_RTMP_STAT_CS(name);
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), ">%uL</bw_",
bw->bandwidth * 8)
- buf);
NGX_RTMP_STAT_CS(name);
NGX_RTMP_STAT_L(">\r\n");
}
NGX_RTMP_STAT_L("<out>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%uL", bw_out->bytes) - buf);
NGX_RTMP_STAT_L("</out>\r\n");
NGX_RTMP_STAT_L("<bwin>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%uL", bw_in->bandwidth * 8) - buf);
NGX_RTMP_STAT_L("</bwin>\r\n");
NGX_RTMP_STAT_L("<bwout>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%uL", bw_out->bandwidth * 8) - buf);
NGX_RTMP_STAT_L("</bwout>\r\n");
if (flags & NGX_RTMP_STAT_BYTES) {
NGX_RTMP_STAT_L("<bytes_");
NGX_RTMP_STAT_CS(name);
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), ">%uL</bytes_",
bw->bytes)
- buf);
NGX_RTMP_STAT_CS(name);
NGX_RTMP_STAT_L(">\r\n");
}
}
@ -297,7 +306,7 @@ ngx_rtmp_stat_get_pool_size(ngx_pool_t *pool, ngx_uint_t *nlarge,
static void
ngx_rtmp_stat_dump_pool(ngx_http_request_t *r, ngx_chain_t ***lll,
ngx_rtmp_stat_dump_pool(ngx_http_request_t *r, ngx_chain_t ***lll,
ngx_pool_t *pool)
{
ngx_uint_t nlarge, size;
@ -321,6 +330,7 @@ ngx_rtmp_stat_client(ngx_http_request_t *r, ngx_chain_t ***lll,
ngx_rtmp_session_t *s)
{
u_char buf[NGX_INT_T_LEN];
struct sockaddr_in *sa;
#ifdef NGX_RTMP_POOL_DEBUG
ngx_rtmp_stat_dump_pool(r, lll, s->connection->pool);
@ -331,9 +341,23 @@ ngx_rtmp_stat_client(ngx_http_request_t *r, ngx_chain_t ***lll,
NGX_RTMP_STAT_L("</id>");
NGX_RTMP_STAT_L("<address>");
NGX_RTMP_STAT_S(&s->connection->addr_text);
NGX_RTMP_STAT_ES(&s->connection->addr_text);
NGX_RTMP_STAT_L("</address>");
/*
** Displays socket port number
*/
NGX_RTMP_STAT_L("<port>");
sa = (struct sockaddr_in *) s->connection->sockaddr;
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>");
NGX_RTMP_STAT_L("<time>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%i",
(ngx_int_t) (ngx_current_msec - s->epoch)) - buf);
@ -356,24 +380,77 @@ ngx_rtmp_stat_client(ngx_http_request_t *r, ngx_chain_t ***lll,
NGX_RTMP_STAT_ES(&s->swf_url);
NGX_RTMP_STAT_L("</swfurl>");
}
NGX_RTMP_STAT_L("<bytes_in>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%ui", (ngx_uint_t) s->in_bytes) - buf);
NGX_RTMP_STAT_L("</bytes_in>");
NGX_RTMP_STAT_L("<bytes_out>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%ui", (ngx_uint_t) s->out_bytes) - buf);
NGX_RTMP_STAT_L("</bytes_out>");
}
static char *
ngx_rtmp_stat_get_aac_profile(ngx_uint_t p, ngx_uint_t sbr, ngx_uint_t ps) {
switch (p) {
case 1:
return "Main";
case 2:
if (ps) {
return "HEv2";
}
if (sbr) {
return "HE";
}
return "LC";
case 3:
return "SSR";
case 4:
return "LTP";
case 5:
return "SBR";
default:
return "";
}
}
static char *
ngx_rtmp_stat_get_avc_profile(ngx_uint_t p) {
switch (p) {
case 66:
return "Baseline";
case 77:
return "Main";
case 100:
return "High";
default:
return "";
}
}
static void
ngx_rtmp_stat_live(ngx_http_request_t *r, ngx_chain_t ***lll,
ngx_rtmp_stat_live(ngx_http_request_t *r, ngx_chain_t ***lll,
ngx_rtmp_live_app_conf_t *lacf)
{
ngx_rtmp_live_stream_t *stream;
ngx_rtmp_codec_ctx_t *codec;
ngx_rtmp_live_ctx_t *ctx;
ngx_rtmp_record_ctx_t *rctx;
ngx_rtmp_record_rec_ctx_t *recctx;
ngx_rtmp_session_t *s;
ngx_int_t n;
ngx_uint_t nclients, total_nclients;
ngx_uint_t nclients, total_nclients, rn;
u_char buf[NGX_INT_T_LEN];
u_char bbuf[NGX_INT32_LEN];
ngx_rtmp_stat_loc_conf_t *slcf;
u_char *cname;
// Is any of stream clients (publisher) recording now
u_char is_recording = 0;
if (!lacf->live) {
return;
}
@ -387,6 +464,8 @@ ngx_rtmp_stat_live(ngx_http_request_t *r, ngx_chain_t ***lll,
for (stream = lacf->streams[n]; stream; stream = stream->next) {
NGX_RTMP_STAT_L("<stream>\r\n");
is_recording = 0;
NGX_RTMP_STAT_L("<name>");
NGX_RTMP_STAT_ECS(stream->name);
NGX_RTMP_STAT_L("</name>\r\n");
@ -397,7 +476,16 @@ ngx_rtmp_stat_live(ngx_http_request_t *r, ngx_chain_t ***lll,
- buf);
NGX_RTMP_STAT_L("</time>");
ngx_rtmp_stat_bw(r, lll, &stream->bw_in, &stream->bw_out);
ngx_rtmp_stat_bw(r, lll, &stream->bw_in, "in",
NGX_RTMP_STAT_BW_BYTES);
ngx_rtmp_stat_bw(r, lll, &stream->bw_out, "out",
NGX_RTMP_STAT_BW_BYTES);
ngx_rtmp_stat_bw(r, lll, &stream->bw_in_audio, "audio",
NGX_RTMP_STAT_BW);
ngx_rtmp_stat_bw(r, lll, &stream->bw_in_video, "video",
NGX_RTMP_STAT_BW);
ngx_rtmp_stat_bw(r, lll, &stream->bw_in_data, "data",
NGX_RTMP_STAT_BW);
nclients = 0;
codec = NULL;
@ -409,18 +497,23 @@ ngx_rtmp_stat_live(ngx_http_request_t *r, ngx_chain_t ***lll,
ngx_rtmp_stat_client(r, lll, s);
NGX_RTMP_STAT_L("<dropped>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%ui", ctx->ndropped) - buf);
NGX_RTMP_STAT_L("</dropped>");
NGX_RTMP_STAT_L("<avsync>");
if (!lacf->interleave) {
NGX_RTMP_STAT(bbuf, ngx_snprintf(bbuf, sizeof(bbuf),
NGX_RTMP_STAT(bbuf, ngx_snprintf(bbuf, sizeof(bbuf),
"%D", ctx->cs[1].timestamp -
ctx->cs[0].timestamp) - bbuf);
}
NGX_RTMP_STAT_L("</avsync>");
NGX_RTMP_STAT_L("<timestamp>");
NGX_RTMP_STAT(bbuf, ngx_snprintf(bbuf, sizeof(bbuf),
"%D", s->current_time) - bbuf);
NGX_RTMP_STAT_L("</timestamp>");
if (ctx->publishing) {
NGX_RTMP_STAT_L("<publishing/>");
}
@ -429,6 +522,18 @@ ngx_rtmp_stat_live(ngx_http_request_t *r, ngx_chain_t ***lll,
NGX_RTMP_STAT_L("<active/>");
}
rctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_record_module);
if (rctx) {
recctx = rctx->rec.elts;
for (rn = 0; rn < rctx->rec.nelts; ++rn, ++recctx) {
if (recctx->initialized && recctx->file.fd != NGX_INVALID_FILE) {
NGX_RTMP_STAT_L("<recording/>");
is_recording = 1;
break;
}
}
}
NGX_RTMP_STAT_L("</client>\r\n");
}
if (ctx->publishing) {
@ -438,43 +543,101 @@ ngx_rtmp_stat_live(ngx_http_request_t *r, ngx_chain_t ***lll,
total_nclients += nclients;
if (codec) {
NGX_RTMP_STAT_L("<meta><width>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
NGX_RTMP_STAT_L("<meta>");
NGX_RTMP_STAT_L("<video>");
NGX_RTMP_STAT_L("<width>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%ui", codec->width) - buf);
NGX_RTMP_STAT_L("</width><height>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%ui", codec->height) - buf);
NGX_RTMP_STAT_L("</height><framerate>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%ui", codec->frame_rate) - buf);
NGX_RTMP_STAT_L("</framerate>");
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><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) {
NGX_RTMP_STAT_L("<video>");
NGX_RTMP_STAT_L("<codec>");
NGX_RTMP_STAT_ECS(cname);
NGX_RTMP_STAT_L("</video>");
NGX_RTMP_STAT_L("</codec>");
}
cname = ngx_rtmp_get_audio_codec_name(codec->audio_codec_id);
if (*cname) {
NGX_RTMP_STAT_L("<audio>");
NGX_RTMP_STAT_ECS(cname);
NGX_RTMP_STAT_L("</audio>");
}
if (*codec->profile) {
if (codec->avc_profile) {
NGX_RTMP_STAT_L("<profile>");
NGX_RTMP_STAT_ECS(codec->profile);
NGX_RTMP_STAT_CS(
ngx_rtmp_stat_get_avc_profile(codec->avc_profile));
NGX_RTMP_STAT_L("</profile>");
}
if (*codec->level) {
if (codec->avc_level) {
NGX_RTMP_STAT_L("<compat>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%ui", codec->avc_compat) - buf);
NGX_RTMP_STAT_L("</compat>");
}
if (codec->avc_level) {
NGX_RTMP_STAT_L("<level>");
NGX_RTMP_STAT_ECS(codec->level);
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%.1f", codec->avc_level / 10.) - buf);
NGX_RTMP_STAT_L("</level>");
}
NGX_RTMP_STAT_L("</video>");
NGX_RTMP_STAT_L("<audio>");
cname = ngx_rtmp_get_audio_codec_name(codec->audio_codec_id);
if (*cname) {
NGX_RTMP_STAT_L("<codec>");
NGX_RTMP_STAT_ECS(cname);
NGX_RTMP_STAT_L("</codec>");
}
if (codec->aac_profile) {
NGX_RTMP_STAT_L("<profile>");
NGX_RTMP_STAT_CS(
ngx_rtmp_stat_get_aac_profile(codec->aac_profile,
codec->aac_sbr,
codec->aac_ps));
NGX_RTMP_STAT_L("</profile>");
}
if (codec->aac_chan_conf) {
NGX_RTMP_STAT_L("<channels>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%ui", codec->aac_chan_conf) - buf);
NGX_RTMP_STAT_L("</channels>");
} else if (codec->audio_channels) {
NGX_RTMP_STAT_L("<channels>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%ui", codec->audio_channels) - buf);
NGX_RTMP_STAT_L("</channels>");
}
if (codec->sample_rate) {
NGX_RTMP_STAT_L("<sample_rate>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%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");
}
NGX_RTMP_STAT_L("<nclients>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%ui", nclients) - buf);
NGX_RTMP_STAT_L("</nclients>\r\n");
@ -486,12 +649,16 @@ ngx_rtmp_stat_live(ngx_http_request_t *r, ngx_chain_t ***lll,
NGX_RTMP_STAT_L("<active/>\r\n");
}
if (is_recording) {
NGX_RTMP_STAT_L("<recording/>\r\n");
}
NGX_RTMP_STAT_L("</stream>\r\n");
}
}
NGX_RTMP_STAT_L("<nclients>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%ui", total_nclients) - buf);
NGX_RTMP_STAT_L("</nclients>\r\n");
@ -500,13 +667,14 @@ ngx_rtmp_stat_live(ngx_http_request_t *r, ngx_chain_t ***lll,
static void
ngx_rtmp_stat_play(ngx_http_request_t *r, ngx_chain_t ***lll,
ngx_rtmp_stat_play(ngx_http_request_t *r, ngx_chain_t ***lll,
ngx_rtmp_play_app_conf_t *pacf)
{
ngx_rtmp_play_ctx_t *ctx, *sctx;
ngx_rtmp_session_t *s;
ngx_uint_t n, nclients, total_nclients;
u_char buf[NGX_INT_T_LEN];
u_char bbuf[NGX_INT32_LEN];
ngx_rtmp_stat_loc_conf_t *slcf;
if (pacf->entries.nelts == 0) {
@ -541,6 +709,11 @@ ngx_rtmp_stat_play(ngx_http_request_t *r, ngx_chain_t ***lll,
ngx_rtmp_stat_client(r, lll, s);
NGX_RTMP_STAT_L("<timestamp>");
NGX_RTMP_STAT(bbuf, ngx_snprintf(bbuf, sizeof(bbuf),
"%D", s->current_time) - bbuf);
NGX_RTMP_STAT_L("</timestamp>");
NGX_RTMP_STAT_L("</client>\r\n");
}
}
@ -557,7 +730,7 @@ ngx_rtmp_stat_play(ngx_http_request_t *r, ngx_chain_t ***lll,
}
NGX_RTMP_STAT_L("<nclients>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%ui", total_nclients) - buf);
NGX_RTMP_STAT_L("</nclients>\r\n");
@ -566,7 +739,7 @@ ngx_rtmp_stat_play(ngx_http_request_t *r, ngx_chain_t ***lll,
static void
ngx_rtmp_stat_application(ngx_http_request_t *r, ngx_chain_t ***lll,
ngx_rtmp_stat_application(ngx_http_request_t *r, ngx_chain_t ***lll,
ngx_rtmp_core_app_conf_t *cacf)
{
ngx_rtmp_stat_loc_conf_t *slcf;
@ -579,12 +752,12 @@ ngx_rtmp_stat_application(ngx_http_request_t *r, ngx_chain_t ***lll,
slcf = ngx_http_get_module_loc_conf(r, ngx_rtmp_stat_module);
if (slcf->stat & NGX_RTMP_STAT_LIVE) {
ngx_rtmp_stat_live(r, lll,
ngx_rtmp_stat_live(r, lll,
cacf->app_conf[ngx_rtmp_live_module.ctx_index]);
}
if (slcf->stat & NGX_RTMP_STAT_PLAY) {
ngx_rtmp_stat_play(r, lll,
ngx_rtmp_stat_play(r, lll,
cacf->app_conf[ngx_rtmp_play_module.ctx_index]);
}
@ -593,7 +766,7 @@ ngx_rtmp_stat_application(ngx_http_request_t *r, ngx_chain_t ***lll,
static void
ngx_rtmp_stat_server(ngx_http_request_t *r, ngx_chain_t ***lll,
ngx_rtmp_stat_server(ngx_http_request_t *r, ngx_chain_t ***lll,
ngx_rtmp_core_srv_conf_t *cscf)
{
ngx_rtmp_core_app_conf_t **cacf;
@ -650,13 +823,20 @@ ngx_rtmp_stat_handler(ngx_http_request_t *r)
NGX_RTMP_STAT_L("<rtmp>\r\n");
#ifdef NGINX_VERSION
NGX_RTMP_STAT_L("<version>" NGINX_VERSION "</version>\r\n");
NGX_RTMP_STAT_L("<nginx_version>" NGINX_VERSION "</nginx_version>\r\n");
#endif
#ifdef NGINX_RTMP_VERSION
NGX_RTMP_STAT_L("<nginx_rtmp_version>" NGINX_RTMP_VERSION "</nginx_rtmp_version>\r\n");
#endif
#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),
@ -673,7 +853,8 @@ ngx_rtmp_stat_handler(ngx_http_request_t *r)
"%ui", ngx_rtmp_naccepted) - nbuf);
NGX_RTMP_STAT_L("</naccepted>\r\n");
ngx_rtmp_stat_bw(r, lll, &ngx_rtmp_bw_in, &ngx_rtmp_bw_out);
ngx_rtmp_stat_bw(r, lll, &ngx_rtmp_bw_in, "in", NGX_RTMP_STAT_BW_BYTES);
ngx_rtmp_stat_bw(r, lll, &ngx_rtmp_bw_out, "out", NGX_RTMP_STAT_BW_BYTES);
cscf = cmcf->servers.elts;
for (n = 0; n < cmcf->servers.nelts; ++n, ++cscf) {

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Roman Arutyunyan
* Copyright (C) Roman Arutyunyan
*/

15
ngx_rtmp_version.h Normal file
View file

@ -0,0 +1,15 @@
/*
* Copyright (C) Roman Arutyunyan
*/
#ifndef _NGX_RTMP_VERSION_H_INCLUDED_
#define _NGX_RTMP_VERSION_H_INCLUDED_
#define nginx_rtmp_version 1002002
#define NGINX_RTMP_VERSION "1.2.2-r1"
#endif /* _NGX_RTMP_VERSION_H_INCLUDED_ */

169
stat.xsl
View file

@ -1,5 +1,11 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright (C) Roman Arutyunyan
-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
@ -11,8 +17,9 @@
<body>
<xsl:apply-templates select="rtmp"/>
<hr/>
Generated by <a href='https://github.com/arut/nginx-rtmp-module'>NGINX RTMP module</a>,
<a href="http://nginx.com">NGINX</a>&#160;<xsl:value-of select="/rtmp/version"/>,
Generated by <a href='https://github.com/arut/nginx-rtmp-module'>
nginx-rtmp-module</a>&#160;<xsl:value-of select="/rtmp/nginx_rtmp_version"/>,
<a href="http://nginx.org">nginx</a>&#160;<xsl:value-of select="/rtmp/nginx_version"/>,
pid <xsl:value-of select="/rtmp/pid"/>,
built <xsl:value-of select="/rtmp/built"/>&#160;<xsl:value-of select="/rtmp/compiler"/>
</body>
@ -24,52 +31,60 @@
<tr bgcolor="#999999">
<th>RTMP</th>
<th>#clients</th>
<th colspan="4">Video</th>
<th colspan="4">Audio</th>
<th>In bytes</th>
<th>Out bytes</th>
<th>Input bits/s</th>
<th>Output bits/s</th>
<th>Size</th>
<th>Frame Rate</th>
<th>Video</th>
<th>Audio</th>
<th>In bits/s</th>
<th>Out bits/s</th>
<th>State</th>
<th>Record</th>
<th>Time</th>
</tr>
<tr>
<td colspan="2">Accepted: <xsl:value-of select="naccepted"/></td>
<th bgcolor="#999999">codec</th>
<th bgcolor="#999999">bits/s</th>
<th bgcolor="#999999">size</th>
<th bgcolor="#999999">fps</th>
<th bgcolor="#999999">codec</th>
<th bgcolor="#999999">bits/s</th>
<th bgcolor="#999999">freq</th>
<th bgcolor="#999999">chan</th>
<td>
<xsl:call-template name="showsize">
<xsl:with-param name="size" select="in"/>
<xsl:with-param name="size" select="bytes_in"/>
</xsl:call-template>
</td>
<td>
<xsl:call-template name="showsize">
<xsl:with-param name="size" select="out"/>
</xsl:call-template>
</td>
<xsl:call-template name="showsize">
<xsl:with-param name="size" select="bytes_out"/>
</xsl:call-template>
</td>
<td>
<xsl:call-template name="showsize">
<xsl:with-param name="size" select="bwin"/>
<xsl:with-param name="bits" select="1"/>
<xsl:with-param name="persec" select="1"/>
</xsl:call-template>
</td>
<td>
<xsl:call-template name="showsize">
<xsl:with-param name="size" select="bwout"/>
<xsl:with-param name="bits" select="1"/>
<xsl:with-param name="persec" select="1"/>
</xsl:call-template>
</td>
<td colspan="5"/>
<td>
<xsl:call-template name="showtime">
<xsl:with-param name="time" select="/rtmp/uptime * 1000"/>
</xsl:call-template>
</td>
</tr>
<xsl:apply-templates select="server"/>
</table>
<xsl:call-template name="showsize">
<xsl:with-param name="size" select="bw_in"/>
<xsl:with-param name="bits" select="1"/>
<xsl:with-param name="persec" select="1"/>
</xsl:call-template>
</td>
<td>
<xsl:call-template name="showsize">
<xsl:with-param name="size" select="bw_out"/>
<xsl:with-param name="bits" select="1"/>
<xsl:with-param name="persec" select="1"/>
</xsl:call-template>
</td>
<td/>
<td/>
<td>
<xsl:call-template name="showtime">
<xsl:with-param name="time" select="/rtmp/uptime * 1000"/>
</xsl:call-template>
</td>
</tr>
<xsl:apply-templates select="server"/>
</table>
</xsl:template>
<xsl:template match="server">
@ -132,43 +147,64 @@
</a>
</td>
<td align="middle"> <xsl:value-of select="nclients"/> </td>
<td>
<xsl:value-of select="meta/video/codec"/>&#160;<xsl:value-of select="meta/video/profile"/>&#160;<xsl:value-of select="meta/video/level"/>
</td>
<td>
<xsl:call-template name="showsize">
<xsl:with-param name="size" select="in"/>
<xsl:with-param name="size" select="bw_video"/>
<xsl:with-param name="bits" select="1"/>
<xsl:with-param name="persec" select="1"/>
</xsl:call-template>
</td>
<td>
<xsl:apply-templates select="meta/video/width"/>
</td>
<td>
<xsl:value-of select="meta/video/frame_rate"/>
</td>
<td>
<xsl:value-of select="meta/audio/codec"/>&#160;<xsl:value-of select="meta/audio/profile"/>
</td>
<td>
<xsl:call-template name="showsize">
<xsl:with-param name="size" select="bw_audio"/>
<xsl:with-param name="bits" select="1"/>
<xsl:with-param name="persec" select="1"/>
</xsl:call-template>
</td>
<td>
<xsl:apply-templates select="meta/audio/sample_rate"/>
</td>
<td>
<xsl:value-of select="meta/audio/channels"/>
</td>
<td>
<xsl:call-template name="showsize">
<xsl:with-param name="size" select="bytes_in"/>
</xsl:call-template>
</td>
<td>
<xsl:call-template name="showsize">
<xsl:with-param name="size" select="out"/>
<xsl:with-param name="size" select="bytes_out"/>
</xsl:call-template>
</td>
<td>
<xsl:call-template name="showsize">
<xsl:with-param name="size" select="bwin"/>
<xsl:with-param name="size" select="bw_in"/>
<xsl:with-param name="bits" select="1"/>
<xsl:with-param name="persec" select="1"/>
</xsl:call-template>
</td>
<td>
<xsl:call-template name="showsize">
<xsl:with-param name="size" select="bwout"/>
<xsl:with-param name="size" select="bw_out"/>
<xsl:with-param name="bits" select="1"/>
<xsl:with-param name="persec" select="1"/>
</xsl:call-template>
</td>
<td>
<xsl:if test="meta/width &gt; 0">
<xsl:value-of select="meta/width"/>x<xsl:value-of select="meta/height"/>
</xsl:if>
</td>
<td align="middle"><xsl:value-of select="meta/framerate"/></td>
<td>
<xsl:value-of select="meta/video"/>
<xsl:apply-templates select="meta/profile"/>
<xsl:apply-templates select="meta/level"/>
</td>
<td><xsl:value-of select="meta/audio"/></td>
<td><xsl:call-template name="streamstate"/></td>
<td><xsl:call-template name="recordstate"/></td>
<td>
<xsl:call-template name="showtime">
<xsl:with-param name="time" select="time"/>
@ -179,16 +215,18 @@
<xsl:attribute name="id">
<xsl:value-of select="../../name"/>-<xsl:value-of select="name"/>
</xsl:attribute>
<td colspan="12" ngcolor="#eeeeee">
<td colspan="16" ngcolor="#eeeeee">
<table cellspacing="1" cellpadding="5">
<tr>
<th>Id</th>
<th>State</th>
<th>Recording</th>
<th>Address</th>
<th>Flash version</th>
<th>Page URL</th>
<th>SWF URL</th>
<th>Dropped</th>
<th>Timestamp</th>
<th>A-V</th>
<th>Time</th>
</tr>
@ -265,6 +303,13 @@
</xsl:choose>
</xsl:template>
<xsl:template name="recordstate">
<xsl:choose>
<xsl:when test="recording">yes</xsl:when>
<xsl:otherwise>no</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="client">
<tr>
@ -276,7 +321,16 @@
</xsl:attribute>
<td><xsl:value-of select="id"/></td>
<td><xsl:call-template name="clientstate"/></td>
<td><xsl:value-of select="address"/></td>
<td><xsl:call-template name="recordstate"/></td>
<td>
<a target="_blank">
<xsl:attribute name="href">
http://apps.db.ripe.net/search/query.html&#63;searchtext=<xsl:value-of select="address"/>
</xsl:attribute>
<xsl:attribute name="title">whois</xsl:attribute>
<xsl:value-of select="address"/>:<xsl:value-of select="port"/>
</a>
</td>
<td><xsl:value-of select="flashver"/></td>
<td>
<a target="_blank">
@ -288,6 +342,7 @@
</td>
<td><xsl:value-of select="swfurl"/></td>
<td><xsl:value-of select="dropped"/></td>
<td><xsl:value-of select="timestamp"/></td>
<td><xsl:value-of select="avsync"/></td>
<td>
<xsl:call-template name="showtime">
@ -305,12 +360,12 @@
active
</xsl:template>
<xsl:template match="profile">
/ <xsl:value-of select="."/>
<xsl:template match="recording">
recording
</xsl:template>
<xsl:template match="level">
/ <xsl:value-of select="."/>
<xsl:template match="width">
<xsl:value-of select="."/>x<xsl:value-of select="../height"/>
</xsl:template>
</xsl:stylesheet>

View file

@ -22,7 +22,7 @@
} else {
playButton.label = 'Play';
videoDisplay.source = "";
videoDisplay.close();
//videoDisplay.close();
}
}
@ -42,6 +42,8 @@
}
private function init():void {
videoDisplay.mx_internal::videoPlayer.bufferTime = 1;
streamer = FlexGlobals.topLevelApplication.parameters.streamer;
if(streamer == null) {
Alert.show('Missing flashvars: streamer');
@ -60,7 +62,7 @@
]]>
</fx:Script>
<s:BorderContainer x="0" y="0" width="100%" height="100%">
<mx:VideoDisplay bufferTime="1" width="100%" height="100%" id="videoDisplay"></mx:VideoDisplay>
<s:VideoDisplay width="100%" height="100%" id="videoDisplay"></s:VideoDisplay>
<s:Button label="Play" id="playButton" horizontalCenter="0" bottom="10"></s:Button>
<s:Button label="[ ]" id="fullscreenButton" right="10" bottom="10"></s:Button>
</s:BorderContainer>

Binary file not shown.

View file

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="500" minHeight="350" creationComplete="init()">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.core.FlexGlobals;
import flash.display.StageDisplayState;
import mx.managers.SystemManager;
import org.osmf.events.MediaPlayerStateChangeEvent;
import org.osmf.events.TimeEvent;
import org.osmf.media.MediaPlayerState;
private var streamer:String;
private var file:String;
private var videoEventsDisabled:Boolean;
private var previousVideoTime:Number;
private function fullscreenListener(event:MouseEvent):void {
try {
switch (stage.displayState) {
case StageDisplayState.FULL_SCREEN:
stage.displayState = StageDisplayState.NORMAL;
break;
default:
stage.displayState = StageDisplayState.FULL_SCREEN;
break;
}
} catch (err:SecurityError) {
Alert.show('Fullsceen error: ' + err);
}
}
private function resetVideo():void {
videoEventsDisabled = true;
try {
videoDisplay.source = "";
} catch (any:*) {}
setTimeout(resetVideoSource, 5000);
}
private function resetVideoSource():void {
videoEventsDisabled = false;
previousVideoTime = NaN;
videoDisplay.source = streamer + "/" + file;
}
protected function stateChangeListener(event:MediaPlayerStateChangeEvent):void {
if (videoEventsDisabled) {
return;
}
if (event.state == MediaPlayerState.PLAYBACK_ERROR) {
resetVideo();
}
}
protected function timeChangeListener(event:TimeEvent):void {
if (videoEventsDisabled) {
return;
}
if (isNaN(event.time) && !isNaN(previousVideoTime)) {
resetVideo();
} else {
previousVideoTime = event.time;
}
}
private function init():void {
videoDisplay.mx_internal::videoPlayer.bufferTime = 1;
streamer = FlexGlobals.topLevelApplication.parameters.streamer;
if(streamer == null) {
Alert.show('Missing flashvars: streamer');
return;
}
file = FlexGlobals.topLevelApplication.parameters.file;
if(file == null) {
Alert.show('Missing flashvars: file');
return;
}
videoDisplay.addEventListener(MouseEvent.DOUBLE_CLICK, fullscreenListener);
videoDisplay.addEventListener("MediaPlayerStateChange", stateChangeListener);
videoDisplay.addEventListener("currentTimeChange", timeChangeListener);
resetVideoSource();
}
]]>
</fx:Script>
<s:BorderContainer x="0" y="0" width="100%" height="100%">
<s:VideoDisplay doubleClickEnabled="true" width="100%" height="100%" id="videoDisplay"></s:VideoDisplay>
</s:BorderContainer>
</s:Application>

Binary file not shown.

View file

@ -67,7 +67,10 @@
camera.setMode(640, 480, 30);
camera.setQuality(131072, 70);
videoDisplay.attachCamera(camera);
//videoDisplay.attachCamera(camera);
var localCam:Video = new Video(640,480);
localCam.attachCamera(camera);
videoDisplay.addChild(localCam);
microphone = Microphone.getMicrophone();
microphone.setSilenceLevel(0);
@ -77,7 +80,7 @@
]]>
</fx:Script>
<s:BorderContainer x="0" y="0" width="100%" height="100%">
<mx:VideoDisplay width="100%" height="100%" id="videoDisplay"></mx:VideoDisplay>
<s:VideoDisplay width="100%" height="100%" id="videoDisplay"></s:VideoDisplay>
<s:Button label="Publish" id="publishButton" horizontalCenter="0" bottom="10"></s:Button>
</s:BorderContainer>
</s:Application>