mirror of
https://github.com/zotanmew/nginx-rtmp-module.git
synced 2024-06-01 22:29:32 +02:00
Compare commits
716 commits
Author | SHA1 | Date | |
---|---|---|---|
Laura Hausmann | a5f44b6d54 | ||
Laura Hausmann | cfb277dea7 | ||
Laura Hausmann | d2d55348d6 | ||
3b7ada6677 | |||
22861b746d | |||
6e40dbe805 | |||
5f8a96f505 | |||
9102003adf | |||
ca9fd4a380 | |||
0668f512b6 | |||
e57666bcf4 | |||
Laura Hausmann | 2fee90d89f | ||
Laura Hausmann | e73d44f0fb | ||
9eefedac83 | |||
c4dcf60e63 | |||
e8ff79dfb9 | |||
8e344d7994 | |||
4d15e2c0f1 | |||
d4d762e917 | |||
5cb0be7f09 | |||
1688a23f0a | |||
a2895a03d9 | |||
649d220306 | |||
ca1f3eeaa2 | |||
a4a1343bb8 | |||
14221340d3 | |||
f7254ae5e8 | |||
fe35e3e98b | |||
cdbb1d4dc1 | |||
d743c36996 | |||
59c2454b23 | |||
6c4be06423 | |||
23ec4ce2d7 | |||
882ef5ca1e | |||
eee3c5eb15 | |||
a0a55be887 | |||
ce5a10a0d1 | |||
3bf7523267 | |||
b2049f3c39 | |||
a5ac72c274 | |||
00fd6cfa53 | |||
b4ee055393 | |||
e2a626ac04 | |||
7b7d30f36c | |||
e5b78f2de7 | |||
a65297410e | |||
669059f41b | |||
ff3536996c | |||
15cc5d0226 | |||
504b9ee29d | |||
23d67822b2 | |||
21db986d97 | |||
a01cc448ee | |||
a898a09d87 | |||
916f3f8374 | |||
1c3dc989ef | |||
d25c56fa69 | |||
e65f2d099b | |||
f31e27fbaf | |||
bb4190e248 | |||
542106e4de | |||
9121b34bdc | |||
ff86f5c3fd | |||
f23323a51a | |||
f8992e572f | |||
4975784d46 | |||
07912c5cd1 | |||
95d81573c9 | |||
bc81475b6b | |||
6b8155cf3b | |||
9c71ce6761 | |||
d86287fe3c | |||
ebe697b601 | |||
dc76eb2641 | |||
18b228a01d | |||
4bf6852a28 | |||
4809496d78 | |||
315e8aa497 | |||
dbcb7aa966 | |||
2fd45d4114 | |||
c47cb2370f | |||
a037181c59 | |||
7381b66e13 | |||
a88bc39141 | |||
998de2937a | |||
a2d65b4251 | |||
26d6107307 | |||
e38fcac9c9 | |||
e4799c633a | |||
7db5ef0ea5 | |||
a9e0056d5b | |||
1d5a20ea2b | |||
eca3fa3b04 | |||
2b0596051e | |||
77ba897d2f | |||
6d9a85e061 | |||
b4ecd58544 | |||
aee81e3c8f | |||
45a02da89e | |||
51396cdebb | |||
358806e915 | |||
14b56c4a5b | |||
0df743179d | |||
965523f397 | |||
fe122c1597 | |||
a48dadfbc1 | |||
341b07409d | |||
970da5673d | |||
570204bdeb | |||
8b97be9593 | |||
c3237ae747 | |||
62748fe56d | |||
1e6ae8d94d | |||
281d2226d9 | |||
89dd74e666 | |||
4f96ff087d | |||
16851c4512 | |||
28f75cb86d | |||
2a6b426247 | |||
d171a0a9b0 | |||
0d94bb2c84 | |||
0bd7d6b375 | |||
307c8d969a | |||
93e9377dc6 | |||
5376bd3432 | |||
98f700a090 | |||
0bfbd6b39f | |||
f15596b8d1 | |||
64c0529fde | |||
01825510f7 | |||
93cf3b69f1 | |||
bfaccfd738 | |||
7eb100a306 | |||
a3924dce67 | |||
66b3bcf096 | |||
65e24b3fee | |||
182566fe93 | |||
e8304c9852 | |||
7e68afde6f | |||
d13e665e56 | |||
2855a9ffc1 | |||
86cfd20b28 | |||
cfadbd7779 | |||
c11797815d | |||
6666d789b5 | |||
ede4b5f0f4 | |||
9f75cc2c6e | |||
2d4613c906 | |||
fc013040b6 | |||
12595a21aa | |||
f9d89634ad | |||
f344f4ae92 | |||
d28e52b32b | |||
c0b592a57c | |||
5e179d7296 | |||
4ce7ea8b9d | |||
292a6c1ca8 | |||
f89d8c1973 | |||
96b69327fa | |||
298697a4da | |||
bf332f3794 | |||
a194707ea9 | |||
cd0d9f73a5 | |||
b9bdd89676 | |||
07a83d8750 | |||
b937376041 | |||
1b7d6148e5 | |||
63c87e8070 | |||
8c5139650d | |||
94343ad786 | |||
3352af8b73 | |||
118b808207 | |||
dc5add30a8 | |||
c206cd7978 | |||
9b25e901f8 | |||
f8da609671 | |||
06e49e05fd | |||
4c7dd6ed00 | |||
c80342e0ab | |||
b6687d6cab | |||
d1e9d8682e | |||
2bc42d90f2 | |||
9ec1c78749 | |||
cd416d5fd2 | |||
74fdeef568 | |||
0f73f4e05b | |||
f9aa396689 | |||
fd23b27f3f | |||
c078a7c3e0 | |||
17d5c4678d | |||
c9442e9aa3 | |||
f9c89a2e21 | |||
f1cde7d4a4 | |||
9faaa99829 | |||
509a9e2f75 | |||
f8097173ab | |||
bacdd48aac | |||
b666598e3a | |||
a344ee6607 | |||
7abda68ef7 | |||
754cca2a58 | |||
0d0882b29d | |||
83cb5a6f90 | |||
5b62a17516 | |||
498ff9a468 | |||
091936ea2d | |||
b4bbfff24b | |||
5d6ea2314f | |||
100ecc4fe8 | |||
8fda94268c | |||
414053862c | |||
f29fbc89a4 | |||
bd154e391b | |||
f0c9b595bc | |||
c39589c5ea | |||
854d5142df | |||
5504e4636c | |||
a92851011a | |||
b1e28ead5b | |||
335bf07021 | |||
24be2c72cd | |||
7f24b5f6cd | |||
bcbef42a7c | |||
b3f0fb0460 | |||
5ed7825c0c | |||
bb67548636 | |||
3497601b6b | |||
38f98aa81c | |||
139c8c2773 | |||
8409e4c499 | |||
cb439496eb | |||
ec3684f9f9 | |||
0327cbf651 | |||
ae7974bc2d | |||
f455876a9c | |||
4e17054a6d | |||
dec648681d | |||
46c0fe6d5a | |||
3207278846 | |||
2a68430ae2 | |||
70d056bf92 | |||
658f26fc2c | |||
4da120a125 | |||
fb80463ba9 | |||
21683a4408 | |||
25146e40f9 | |||
e9f58daf81 | |||
3170037e74 | |||
dc0b3df1b7 | |||
52b09f5300 | |||
ac4197b871 | |||
b49c7fb6ea | |||
5d4dfaee43 | |||
1e5b135b2b | |||
889e6afccb | |||
99cf8a1a10 | |||
1055727de8 | |||
de8faa55a7 | |||
c9e70d5994 | |||
593368e9e6 | |||
a05b3babfe | |||
961fde23f7 | |||
75fe5406b4 | |||
1616a378ef | |||
4c1b69b5bb | |||
038c3bbc67 | |||
0d77210815 | |||
c4ee944870 | |||
69d79ccda6 | |||
859540db60 | |||
087e5358e2 | |||
d797d269a4 | |||
95471ae880 | |||
a079875fd7 | |||
8341644121 | |||
098ded3e87 | |||
70c90bbac5 | |||
a4907e80ce | |||
48983747d3 | |||
6ce821727d | |||
809f094124 | |||
2c2e90b706 | |||
b053eca1f9 | |||
b92a262f4c | |||
955c7e6b64 | |||
f62a083806 | |||
cb36f9c23c | |||
1d1d4fcac6 | |||
459e730d34 | |||
8f0f2e75c9 | |||
17159755e5 | |||
de42f3801d | |||
1d21d68c3c | |||
2a54c8e089 | |||
d069f36f86 | |||
99433754df | |||
2362acf45c | |||
ad6c63c149 | |||
c54b413b43 | |||
7a35372e30 | |||
d99c069e8e | |||
5fb4c99ca9 | |||
e1f92b1409 | |||
dd5f2aa117 | |||
d574043a96 | |||
c390521963 | |||
83dc27d30a | |||
2262649c1f | |||
9ebfcc1978 | |||
0bb2323990 | |||
7500b4bd90 | |||
eb1cfef69e | |||
0b3d545ff6 | |||
5e1d735992 | |||
997f24c5c6 | |||
8099f44828 | |||
51ab327abf | |||
69c090d85f | |||
2f82fa2e8e | |||
773336e497 | |||
359d76b555 | |||
4bed919cbe | |||
416931d631 | |||
eff973ce01 | |||
53064a48f1 | |||
80d7b1c905 | |||
8acacd0d79 | |||
607a53842a | |||
812e2fd7b1 | |||
8c2229cce5 | |||
a72e33ea41 | |||
4e780909b9 | |||
876de488b8 | |||
abb017225b | |||
96855b1413 | |||
6f768dc4eb | |||
668125888b | |||
b31539ce95 | |||
0f337fe9a4 | |||
a43bafe3de | |||
dcf37b2c39 | |||
87bc473cde | |||
c76d0fad44 | |||
0210f7ca83 | |||
521f8998b0 | |||
434e6cfc06 | |||
04cbad5fda | |||
8d1b1c1243 | |||
1474aec731 | |||
78dcfbcc35 | |||
546c42efeb | |||
2eb36e3d7d | |||
acc1aa789f | |||
fac68de376 | |||
51c1459e04 | |||
39717828d6 | |||
a8094dfef1 | |||
8d9f99b540 | |||
036a7fd1d5 | |||
c392252301 | |||
65cd61e433 | |||
8a8f28f3b6 | |||
ea1fe6eaf5 | |||
8e62c72e86 | |||
8608faad1e | |||
5e5dd797c2 | |||
ee55c21b4c | |||
a8d148473d | |||
8d28f7f1de | |||
0166380bef | |||
0aaf2a6259 | |||
7dd70f88ea | |||
5507823b9d | |||
5c8d5b34fb | |||
0a33c0575e | |||
2f6a4fa279 | |||
13854403e2 | |||
5e8fe20497 | |||
b8b055dd77 | |||
95075aaa2a | |||
732b44883c | |||
f53d158432 | |||
7e19dce7c4 | |||
fe0d805179 | |||
ef424df677 | |||
57a96ac2bc | |||
5b68307710 | |||
307245415b | |||
095c659ad8 | |||
f45c0d50db | |||
0d1e1fec7c | |||
8542e21e17 | |||
b77337edab | |||
bcf7df1e70 | |||
61cb334917 | |||
6bb620f2fc | |||
da0128a2fc | |||
471d299f5d | |||
f39d3f66a3 | |||
0a5b06609b | |||
3a5f9eea78 | |||
18fa7a5016 | |||
aa4bcf89d7 | |||
7eb0f4e2d1 | |||
d98b24c4b7 | |||
e8081fd94f | |||
b84e00d5bf | |||
9e634ae882 | |||
51c80e2179 | |||
31f4788835 | |||
fb4793f48a | |||
38126a2c3f | |||
775f4e0682 | |||
bd73e06884 | |||
6f2e105800 | |||
4fd9c6c15c | |||
1b5e19d4e7 | |||
831823a772 | |||
b9fcf7d880 | |||
96e60a4ac0 | |||
6e1008ee9c | |||
047b72c192 | |||
d01ffc0c88 | |||
5bd02e7ed3 | |||
b07e0e2dee | |||
2f54340454 | |||
45b39f7762 | |||
ed09425a5d | |||
43ececd070 | |||
0eb32ae1ca | |||
d11823bcf8 | |||
7c7d1f1db2 | |||
43718388f4 | |||
c928742cb9 | |||
ec5687f459 | |||
46edc6ccfd | |||
4b3385562a | |||
fb3adeda9f | |||
a1b1c205f1 | |||
749cea7cb7 | |||
9abf8355d9 | |||
fcbd18c7f7 | |||
29dfda882a | |||
27df4c10af | |||
b73075e28c | |||
ed68e4a2eb | |||
15ec66a7bf | |||
995688b9cf | |||
a933495bf1 | |||
935156c352 | |||
ba94a0c1e4 | |||
33240c33df | |||
f7d65ba915 | |||
a95065b90d | |||
9227aca90d | |||
884366a2e0 | |||
f6af3f2343 | |||
24addede15 | |||
85dbf397e4 | |||
81ce4788f9 | |||
20584b05b8 | |||
bec6d8553a | |||
1d421c85f4 | |||
87ee276489 | |||
a9ddfed095 | |||
0dca45f0b6 | |||
8c0b83514c | |||
2c7111cf10 | |||
3b680915a8 | |||
df33838e37 | |||
fe4877f216 | |||
37033ed8c0 | |||
85d34da0d4 | |||
c18fd3aad4 | |||
6caee7cb88 | |||
0e07492797 | |||
7ea7690db2 | |||
6067ac21e0 | |||
748192877a | |||
5a38108bad | |||
503375fea8 | |||
0fb3ce40e0 | |||
8f9965b7f2 | |||
91c69aed28 | |||
401d7e929c | |||
1cfb7aeb58 | |||
b564a096fd | |||
8856d11dd9 | |||
f599c70568 | |||
a6913ea68a | |||
893e17343a | |||
040c96ed9b | |||
9177be55dd | |||
b62651efbd | |||
055228cc8a | |||
0ac369cd5a | |||
2f3ff168c3 | |||
c89459b784 | |||
f8588a450f | |||
97d144ae28 | |||
737ea5ce34 | |||
05548c515f | |||
857eca9a61 | |||
2060dccbf6 | |||
a5a59aa50c | |||
940ff26009 | |||
67443c28b4 | |||
f052e4cc48 | |||
0f8641d0c8 | |||
fa2b5b037f | |||
5eb6dce453 | |||
face62aa34 | |||
49a3ee0dc6 | |||
db3fc521d8 | |||
c442e1fc2d | |||
b4e77fa750 | |||
7b4df729c1 | |||
a3cffbb6c2 | |||
c11b916501 | |||
d7b4f138a4 | |||
488c571497 | |||
b52ebc8534 | |||
6b92cd6b29 | |||
0b5c799b25 | |||
337714edef | |||
2f1091679b | |||
4ec43349ad | |||
489f5cf5db | |||
e39b7f62a5 | |||
9a5e9a7790 | |||
9956185950 | |||
0f95890a73 | |||
8696ca005f | |||
afc765560f | |||
daa1c66e64 | |||
cbe760aa8a | |||
c4e78b6ea9 | |||
01c8daf4b4 | |||
4dcb52ae1a | |||
7e517f1619 | |||
3f045ccdca | |||
61dc8afa65 | |||
308dbd47d1 | |||
388d735460 | |||
47cdfc4b67 | |||
470c3effc8 | |||
d60c0bf1c7 | |||
14ba61505f | |||
cbd7a71723 | |||
0c35b73152 | |||
3b4452d43e | |||
dc1afb50be | |||
ec68e7c2d5 | |||
95447b3dc6 | |||
c41d4e7810 | |||
1bd6aed4c8 | |||
49f8f4d097 | |||
56b54b1215 | |||
161e36ee9d | |||
cb2a18a3ec | |||
248335216a | |||
9389670657 | |||
c752e0696c | |||
479a574cec | |||
8c81d3ef39 | |||
db34613f1a | |||
e9a9c5f4bd | |||
b93e2cdff6 | |||
e4070fb777 | |||
8553cf204b | |||
f94fcee9fb | |||
021db6cfd9 | |||
f114ce4ce9 | |||
289ee42c53 | |||
613234b866 | |||
22984bc309 | |||
e29e64ed7d | |||
3d70ba4777 | |||
a47b23204b | |||
58bd602946 | |||
aeb20d0a24 | |||
849dbf6b89 | |||
244961076f | |||
db5d361743 | |||
6fa61c090e | |||
8a332610bc | |||
beb76aa8d5 | |||
36d744d257 | |||
8958f0b33d | |||
11e14ee928 | |||
1d30518c8f | |||
ed98ca42ef | |||
f4d974425c | |||
09db8210a7 | |||
de2a4258d7 | |||
73f94b99ff | |||
97a5a860e2 | |||
dd75b749da | |||
d0fb258d10 | |||
b0de943956 | |||
7688a62874 | |||
c61e99a368 | |||
d422c3685d | |||
f79aa206ec | |||
6dc5b12f03 | |||
0ad74ac2bf | |||
2bb16425da | |||
c0fc4bbdcc | |||
c2fcd8c09c | |||
5db5d5af24 | |||
f35e965033 | |||
7b5c5e9932 | |||
0958e4f034 | |||
76f433fb45 | |||
b9e5bb6ef0 | |||
61b9c53972 | |||
d393508e58 | |||
d34c6393f6 | |||
3fc1598db9 | |||
709b173bdc | |||
dae4c77e93 | |||
2e6d8b219a | |||
a3db04dd5b | |||
9b24a2f11a | |||
f9532fea8c | |||
6a1cf17dcb | |||
daae21764e | |||
05e0a68281 | |||
5d8a534aa4 | |||
0bd07562c5 | |||
921b28112c | |||
059dba448d | |||
814bcf4825 | |||
eee52d83c4 | |||
682d24d682 | |||
92310a7240 | |||
e68add616c | |||
612fc4dd75 | |||
9431058e3e | |||
47f74f7968 | |||
7f7fcc8d5c | |||
15fe840681 | |||
1a0dd69f34 | |||
9aa030f51c | |||
6298aa7e1d | |||
e5d2df6fc0 | |||
0a8f0ee6c5 | |||
2986cb7301 | |||
11e3f53fd2 | |||
5d28b1b44c | |||
5d3189ad97 | |||
1894d333aa | |||
368466b0ea | |||
018daa47b5 | |||
6a848ea47c | |||
e22ca28604 | |||
b96bcfc4ed | |||
4eca0892a3 | |||
8cc2a64895 | |||
ac5df69487 | |||
b40024492e | |||
f619e072ff | |||
53e68df5a9 | |||
e916ca9ad0 | |||
0e01665e0b | |||
4155794f45 | |||
3e25f91004 | |||
3aa528fccd | |||
6923889e53 | |||
57dd1406bd | |||
8526a59fd4 | |||
ee4f57bc69 | |||
0b6a84b0b8 | |||
af8446a098 | |||
349259329d | |||
43539b7234 | |||
6903ac23e4 | |||
e28dfad05c | |||
4151532589 | |||
6db8ab7963 | |||
138c330da2 | |||
5e55f62b88 | |||
561c421f54 | |||
2aabf02fe4 | |||
5715784e7c | |||
6b96942097 | |||
8104bff06f | |||
6d5986aa59 | |||
36002359c2 | |||
d59438d92d | |||
5325df0135 | |||
f4b29b0547 | |||
c514c9f715 | |||
c0ad999f37 | |||
027c65cd8f | |||
8c39b37131 | |||
2741432356 | |||
f358a2e8f9 | |||
2a9d41496a | |||
aefd113e8d | |||
98efe9ed7f | |||
b2ba2680a3 | |||
67aa7d5d0d | |||
eb1baa936e | |||
2c235d8633 | |||
f89bbae235 | |||
0153d0865b | |||
00a640d0d6 | |||
5d00a5bb6c | |||
aeae84bf8b | |||
af5703b357 | |||
9c88e12cf8 | |||
2ed36a2611 | |||
3bd60857bd | |||
54d9d41bf0 | |||
18e4762db2 |
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
/.idea
|
||||
/.settings
|
||||
/.project
|
||||
/.cproject
|
||||
/.vscode
|
7
AUTHORS
7
AUTHORS
|
@ -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
|
||||
|
|
8
LICENSE
8
LICENSE
|
@ -1,14 +1,14 @@
|
|||
Copyright (c) 2012, 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
|
||||
|
|
160
README.md
160
README.md
|
@ -1,14 +1,29 @@
|
|||
# NGINX-based RTMP server
|
||||
## nginx-rtmp-module
|
||||
# NGINX-based Media Streaming Server
|
||||
|
||||
## nginx-rtmp-module
|
||||
|
||||
### Project blog
|
||||
|
||||
http://rarut.wordpress.com
|
||||
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
|
||||
|
||||
|
@ -16,18 +31,15 @@
|
|||
|
||||
https://groups.google.com/group/nginx-rtmp-ru (Russian)
|
||||
|
||||
|
||||
### Project page at github
|
||||
|
||||
Project features, examples, donation information
|
||||
### Donation page (Paypal etc)
|
||||
|
||||
http://arut.github.com/nginx-rtmp-module/
|
||||
|
||||
### 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
|
||||
|
@ -39,10 +51,6 @@
|
|||
|
||||
* Online transcoding with FFmpeg
|
||||
|
||||
* HLS (HTTP Live Streaming) support;
|
||||
requires recent libavformat
|
||||
(>= 53.31.100) from ffmpeg (ffmpeg.org)
|
||||
|
||||
* HTTP callbacks (publish/play/record/update etc)
|
||||
|
||||
* Running external programs on certain events (exec)
|
||||
|
@ -54,28 +62,48 @@
|
|||
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-
|
||||
readable form
|
||||
|
||||
* Linux/FreeBSD/MacOS
|
||||
|
||||
* Linux/FreeBSD/MacOS/Windows
|
||||
|
||||
### Build
|
||||
|
||||
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
|
||||
|
||||
See this article about building nginx-rtmp with HLS support:
|
||||
https://github.com/arut/nginx-rtmp-module/wiki/Building-nginx-rtmp-with-HLS-support
|
||||
Several versions of nginx (1.3.14 - 1.5.0) require http_ssl_module to be
|
||||
added as well:
|
||||
|
||||
./configure --add-module=/path/to/nginx-rtmp-module --with-http_ssl_module
|
||||
|
||||
For building debug version of nginx add `--with-debug`
|
||||
|
||||
./configure --add-module=/path/to-nginx/rtmp-module --with-debug
|
||||
|
||||
[Read more about debug log](https://github.com/arut/nginx-rtmp-module/wiki/Debug-log)
|
||||
|
||||
### Contributing and Branch Policy
|
||||
|
||||
The "dev" branch is the one where all contributions will be merged before reaching "master".
|
||||
If you plan to propose a patch, please commit into the "dev" branch or its own feature branch.
|
||||
Direct commit to "master" are not permitted.
|
||||
|
||||
### Windows limitations
|
||||
|
||||
Windows support is limited. These features are not supported
|
||||
|
||||
* execs
|
||||
* static pulls
|
||||
* auto_push
|
||||
|
||||
### RTMP URL format
|
||||
|
||||
|
@ -90,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
|
||||
|
@ -131,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 &
|
||||
|
@ -143,7 +169,8 @@ rtmp_auto_push directive.
|
|||
#
|
||||
# Multiple exec lines can be specified.
|
||||
|
||||
exec /usr/bin/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 {
|
||||
|
@ -151,11 +178,19 @@ rtmp_auto_push directive.
|
|||
# Video with reduced resolution comes here from ffmpeg
|
||||
}
|
||||
|
||||
application webcam {
|
||||
live on;
|
||||
|
||||
# Stream from local webcam
|
||||
exec_static ffmpeg -f video4linux2 -i /dev/video0 -c:v libx264 -an
|
||||
-f flv rtmp://localhost:1935/webcam/mystream;
|
||||
}
|
||||
|
||||
application mypush {
|
||||
live on;
|
||||
|
||||
# Every stream published here
|
||||
# is automatically pushed to
|
||||
# is automatically pushed to
|
||||
# these two machines
|
||||
push rtmp1.example.com;
|
||||
push rtmp2.example.com:1934;
|
||||
|
@ -169,6 +204,13 @@ rtmp_auto_push directive.
|
|||
pull rtmp://rtmp3.example.com pageUrl=www.example.com/index.html;
|
||||
}
|
||||
|
||||
application mystaticpull {
|
||||
live on;
|
||||
|
||||
# Static pull is started at nginx start
|
||||
pull rtmp://rtmp4.example.com pageUrl=www.example.com/index.html name=mystream static;
|
||||
}
|
||||
|
||||
# video on demand
|
||||
application vod {
|
||||
play /var/flvs;
|
||||
|
@ -184,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
|
||||
|
||||
|
@ -201,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.
|
||||
|
@ -222,20 +264,16 @@ rtmp_auto_push directive.
|
|||
|
||||
# HLS
|
||||
|
||||
# HLS requires libavformat & should be configured as a separate
|
||||
# NGINX module in addition to nginx-rtmp-module:
|
||||
# ./configure ... --add-module=/path/to/nginx-rtmp-module/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)
|
||||
#
|
||||
# Incoming stream must be in H264/AAC/MP3. For iPhones use baseline H264
|
||||
# Incoming stream must be in H264/AAC. For iPhones use baseline H264
|
||||
# profile (see ffmpeg example).
|
||||
# This example creates RTMP stream from movie ready for HLS:
|
||||
#
|
||||
# ffmpeg -loglevel verbose -re -i movie.avi -vcodec libx264
|
||||
# -vprofile baseline -acodec libmp3lame -ar 44100 -ac 1
|
||||
# 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.
|
||||
|
@ -243,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -275,28 +319,18 @@ rtmp_auto_push directive.
|
|||
|
||||
location /hls {
|
||||
# Serve HLS fragments
|
||||
alias /tmp/app;
|
||||
types {
|
||||
application/vnd.apple.mpegurl m3u8;
|
||||
video/mp2t ts;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
20
TODO
20
TODO
|
@ -1,20 +0,0 @@
|
|||
- multiple tracks in vod/mp4
|
||||
|
||||
- HLS problem on publish restart
|
||||
|
||||
|
||||
- improve session epoch variable
|
||||
|
||||
- exec_init
|
||||
|
||||
- fix auto-pushing to cache manager
|
||||
|
||||
- HDS
|
||||
|
||||
- DNS round-robin url
|
||||
|
||||
- multiple streams per connection
|
||||
|
||||
- VOD HLS
|
||||
|
||||
- sync exec
|
75
config
75
config
|
@ -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,35 +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 \
|
||||
|
@ -63,17 +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
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
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
52
dash/ngx_rtmp_mp4.h
Normal 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_ */
|
|
@ -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
94
doc/control_modul.md
Normal 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
16
doc/debug_log.md
Normal 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
1824
doc/directives.md
Normal file
File diff suppressed because it is too large
Load diff
69
doc/examples.md
Normal file
69
doc/examples.md
Normal 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
|
||||
```
|
33
doc/exec_wrapper_in_bash.md
Normal file
33
doc/exec_wrapper_in_bash.md
Normal 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
26
doc/faq.md
Normal 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;
|
||||
```
|
38
doc/getting_number_of_subscribers.md
Normal file
38
doc/getting_number_of_subscribers.md
Normal 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
188
doc/getting_started.md
Normal 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.
|
24
doc/installing_in_gentoo.md
Normal file
24
doc/installing_in_gentoo.md
Normal 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)
|
30
doc/installing_ubuntu_using_ppas.md
Normal file
30
doc/installing_ubuntu_using_ppas.md
Normal 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
114
doc/tutorial.md
Normal 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
|
|
@ -1,9 +0,0 @@
|
|||
# HLS (HTTP Live Streaming) module
|
||||
|
||||
This module should be added explicitly when building NGINX:
|
||||
|
||||
./configure ... --add-module=/path/to/nginx-rtmp-module/hls ...
|
||||
|
||||
## Requirement
|
||||
|
||||
The module requires ffmpeg version>= 53.31.100 from ffmpeg (ffmpeg.org)
|
13
hls/config
13
hls/config
|
@ -1,13 +0,0 @@
|
|||
ngx_addon_name="ngx_rtmp_hls_module"
|
||||
|
||||
CORE_MODULES="$CORE_MODULES
|
||||
ngx_rtmp_hls_module \
|
||||
"
|
||||
|
||||
|
||||
NGX_ADDON_SRCS="$NGX_ADDON_SRCS \
|
||||
$ngx_addon_dir/ngx_rtmp_hls_module.c \
|
||||
"
|
||||
|
||||
CORE_LIBS="$CORE_LIBS -lavformat -lavcodec -lavutil"
|
||||
|
File diff suppressed because it is too large
Load diff
486
hls/ngx_rtmp_mpegts.c
Normal file
486
hls/ngx_rtmp_mpegts.c
Normal file
|
@ -0,0 +1,486 @@
|
|||
|
||||
/*
|
||||
* 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[] = {
|
||||
|
||||
/* 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, // 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,
|
||||
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, 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, 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, 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, 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, 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)
|
||||
|
||||
/* PMT */
|
||||
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,
|
||||
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, 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, 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, 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, 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, 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
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_mpegts_write_file(ngx_rtmp_mpegts_file_t *file, u_char *in,
|
||||
size_t in_size)
|
||||
{
|
||||
u_char *out;
|
||||
size_t out_size, n;
|
||||
ssize_t rc;
|
||||
|
||||
static u_char buf[1024];
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
static u_char *
|
||||
ngx_rtmp_mpegts_write_pcr(u_char *p, uint64_t pcr)
|
||||
{
|
||||
*p++ = (u_char) (pcr >> 25);
|
||||
*p++ = (u_char) (pcr >> 17);
|
||||
*p++ = (u_char) (pcr >> 9);
|
||||
*p++ = (u_char) (pcr >> 1);
|
||||
*p++ = (u_char) (pcr << 7 | 0x7e);
|
||||
*p++ = 0;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
static u_char *
|
||||
ngx_rtmp_mpegts_write_pts(u_char *p, ngx_uint_t fb, uint64_t pts)
|
||||
{
|
||||
ngx_uint_t val;
|
||||
|
||||
val = fb << 4 | (((pts >> 30) & 0x07) << 1) | 1;
|
||||
*p++ = (u_char) val;
|
||||
|
||||
val = (((pts >> 15) & 0x7fff) << 1) | 1;
|
||||
*p++ = (u_char) (val >> 8);
|
||||
*p++ = (u_char) val;
|
||||
|
||||
val = (((pts) & 0x7fff) << 1) | 1;
|
||||
*p++ = (u_char) (val >> 8);
|
||||
*p++ = (u_char) val;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
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_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,
|
||||
(ngx_uint_t) f->key, (size_t) (b->last - b->pos));
|
||||
|
||||
first = 1;
|
||||
|
||||
while (b->pos < b->last) {
|
||||
p = packet;
|
||||
|
||||
f->cc++;
|
||||
|
||||
*p++ = 0x47;
|
||||
*p++ = (u_char) (f->pid >> 8);
|
||||
|
||||
if (first) {
|
||||
p[-1] |= 0x40;
|
||||
}
|
||||
|
||||
*p++ = (u_char) f->pid;
|
||||
*p++ = 0x10 | (f->cc & 0x0f); /* payload */
|
||||
|
||||
if (first) {
|
||||
|
||||
packet[3] |= 0x20; /* adaptation */
|
||||
|
||||
*p++ = 7; /* size */
|
||||
*p++ = 0x50; /* random access + PCR */
|
||||
|
||||
p = ngx_rtmp_mpegts_write_pcr(p, f->dts - NGX_RTMP_HLS_DELAY);
|
||||
|
||||
/* PES header */
|
||||
|
||||
*p++ = 0x00;
|
||||
*p++ = 0x00;
|
||||
*p++ = 0x01;
|
||||
*p++ = (u_char) f->sid;
|
||||
|
||||
header_size = 5;
|
||||
flags = 0x80; /* PTS */
|
||||
|
||||
if (f->dts != f->pts) {
|
||||
header_size += 5;
|
||||
flags |= 0x40; /* DTS */
|
||||
}
|
||||
|
||||
pes_size = (b->last - b->pos) + header_size + 3;
|
||||
if (pes_size > 0xffff) {
|
||||
pes_size = 0;
|
||||
}
|
||||
|
||||
*p++ = (u_char) (pes_size >> 8);
|
||||
*p++ = (u_char) pes_size;
|
||||
*p++ = 0x80; /* H222 */
|
||||
*p++ = (u_char) flags;
|
||||
*p++ = (u_char) header_size;
|
||||
|
||||
p = ngx_rtmp_mpegts_write_pts(p, flags >> 6, f->pts +
|
||||
NGX_RTMP_HLS_DELAY);
|
||||
|
||||
if (f->dts != f->pts) {
|
||||
p = ngx_rtmp_mpegts_write_pts(p, 1, f->dts +
|
||||
NGX_RTMP_HLS_DELAY);
|
||||
}
|
||||
|
||||
first = 0;
|
||||
}
|
||||
|
||||
body_size = (ngx_uint_t) (packet + sizeof(packet) - p);
|
||||
in_size = (ngx_uint_t) (b->last - b->pos);
|
||||
|
||||
if (body_size <= in_size) {
|
||||
ngx_memcpy(p, b->pos, body_size);
|
||||
b->pos += body_size;
|
||||
|
||||
} else {
|
||||
stuff_size = (body_size - in_size);
|
||||
|
||||
if (packet[3] & 0x20) {
|
||||
|
||||
/* has adaptation */
|
||||
|
||||
base = &packet[5] + packet[4];
|
||||
p = ngx_movemem(base + stuff_size, base, p - base);
|
||||
ngx_memset(base, 0xff, stuff_size);
|
||||
packet[4] += (u_char) stuff_size;
|
||||
|
||||
} else {
|
||||
|
||||
/* no adaptation */
|
||||
|
||||
packet[3] |= 0x20;
|
||||
p = ngx_movemem(&packet[4] + stuff_size, &packet[4],
|
||||
p - &packet[4]);
|
||||
|
||||
packet[4] = (u_char) (stuff_size - 1);
|
||||
if (stuff_size >= 2) {
|
||||
packet[5] = 0;
|
||||
ngx_memset(&packet[6], 0xff, stuff_size - 2);
|
||||
}
|
||||
}
|
||||
|
||||
ngx_memcpy(p, b->pos, in_size);
|
||||
b->pos = b->last;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
48
hls/ngx_rtmp_mpegts.h
Normal file
48
hls/ngx_rtmp_mpegts.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
|
||||
/*
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_RTMP_MPEGTS_H_INCLUDED_
|
||||
#define _NGX_RTMP_MPEGTS_H_INCLUDED_
|
||||
|
||||
|
||||
#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 {
|
||||
uint64_t pts;
|
||||
uint64_t dts;
|
||||
ngx_uint_t pid;
|
||||
ngx_uint_t sid;
|
||||
ngx_uint_t cc;
|
||||
unsigned key:1;
|
||||
} ngx_rtmp_mpegts_frame_t;
|
||||
|
||||
|
||||
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
80
hls/ngx_rtmp_mpegts_crc.c
Normal 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
83
hls/ngx_rtmp_mpegts_crc.h
Normal 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_ */
|
102
ngx_rtmp.c
102
ngx_rtmp.c
|
@ -1,11 +1,13 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_event.h>
|
||||
#include <nginx.h>
|
||||
#include "ngx_rtmp.h"
|
||||
|
||||
|
||||
|
@ -20,13 +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;
|
||||
|
@ -59,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 */
|
||||
|
@ -75,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;
|
||||
|
@ -89,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++;
|
||||
}
|
||||
|
||||
|
||||
|
@ -136,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);
|
||||
|
@ -169,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) {
|
||||
|
@ -200,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 */
|
||||
|
||||
|
@ -242,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) {
|
||||
|
@ -271,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) {
|
||||
|
@ -338,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) {
|
||||
|
@ -358,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;
|
||||
|
@ -428,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;
|
||||
|
@ -438,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;
|
||||
|
@ -539,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;
|
||||
|
@ -682,7 +700,11 @@ ngx_rtmp_add_addrs(ngx_conf_t *cf, ngx_rtmp_port_t *mport,
|
|||
|
||||
addrs[i].conf.ctx = addr[i].ctx;
|
||||
|
||||
len = ngx_sock_ntop(addr[i].sockaddr, buf, NGX_SOCKADDR_STRLEN, 1);
|
||||
len = ngx_sock_ntop(addr[i].sockaddr,
|
||||
#if (nginx_version >= 1005003)
|
||||
addr[i].socklen,
|
||||
#endif
|
||||
buf, NGX_SOCKADDR_STRLEN, 1);
|
||||
|
||||
p = ngx_pnalloc(cf->pool, len);
|
||||
if (p == NULL) {
|
||||
|
@ -693,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;
|
||||
|
@ -727,7 +750,11 @@ ngx_rtmp_add_addrs6(ngx_conf_t *cf, ngx_rtmp_port_t *mport,
|
|||
|
||||
addrs6[i].conf.ctx = addr[i].ctx;
|
||||
|
||||
len = ngx_sock_ntop(addr[i].sockaddr, buf, NGX_SOCKADDR_STRLEN, 1);
|
||||
len = ngx_sock_ntop(addr[i].sockaddr,
|
||||
#if (nginx_version >= 1005003)
|
||||
addr[i].socklen,
|
||||
#endif
|
||||
buf, NGX_SOCKADDR_STRLEN, 1);
|
||||
|
||||
p = ngx_pnalloc(cf->pool, len);
|
||||
if (p == NULL) {
|
||||
|
@ -738,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;
|
||||
|
@ -811,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;
|
||||
}
|
||||
|
|
129
ngx_rtmp.h
129
ngx_rtmp.h
|
@ -1,5 +1,6 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
|
@ -11,11 +12,18 @@
|
|||
#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"
|
||||
|
||||
|
||||
#if (NGX_WIN32)
|
||||
typedef __int8 int8_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
void **main_conf;
|
||||
void **srv_conf;
|
||||
|
@ -36,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;
|
||||
|
@ -47,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
|
||||
|
@ -90,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;
|
||||
|
@ -167,6 +178,14 @@ typedef struct {
|
|||
} ngx_rtmp_stream_t;
|
||||
|
||||
|
||||
/* disable zero-sized array warning by msvc */
|
||||
|
||||
#if (NGX_WIN32)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4200)
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint32_t signature; /* "RTMP" */ /* <-- FIXME wtf */
|
||||
|
||||
|
@ -180,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;
|
||||
|
@ -188,6 +211,7 @@ typedef struct {
|
|||
|
||||
/* connection parameters */
|
||||
ngx_str_t app;
|
||||
ngx_str_t args;
|
||||
ngx_str_t flashver;
|
||||
ngx_str_t swf_url;
|
||||
ngx_str_t tc_url;
|
||||
|
@ -205,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;
|
||||
|
@ -214,6 +242,7 @@ typedef struct {
|
|||
/* auto-pushed? */
|
||||
unsigned auto_pushed:1;
|
||||
unsigned relay:1;
|
||||
unsigned static_relay:1;
|
||||
|
||||
/* input stream 0 (reserved by RTMP spec)
|
||||
* is used as free chain link */
|
||||
|
@ -243,6 +272,11 @@ typedef struct {
|
|||
} ngx_rtmp_session_t;
|
||||
|
||||
|
||||
#if (NGX_WIN32)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
|
||||
/* handler result code:
|
||||
* NGX_ERROR - error
|
||||
* NGX_OK - success, may continue
|
||||
|
@ -284,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;
|
||||
|
@ -295,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;
|
||||
|
@ -321,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);
|
||||
|
@ -367,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);
|
||||
|
@ -389,24 +424,25 @@ void * ngx_rtmp_rmemcpy(void *dst, const void* src, size_t n);
|
|||
(((u_char*)ngx_rtmp_rmemcpy(dst, src, n)) + (n))
|
||||
|
||||
|
||||
static inline uint16_t
|
||||
static ngx_inline uint16_t
|
||||
ngx_rtmp_r16(uint16_t n)
|
||||
{
|
||||
return (n << 8) | (n >> 8);
|
||||
}
|
||||
|
||||
|
||||
static inline uint32_t
|
||||
static ngx_inline uint32_t
|
||||
ngx_rtmp_r32(uint32_t n)
|
||||
{
|
||||
return (n << 24) | ((n << 8) & 0xff0000) | ((n >> 8) & 0xff00) | (n >> 24);
|
||||
}
|
||||
|
||||
|
||||
static inline uint64_t
|
||||
static ngx_inline uint64_t
|
||||
ngx_rtmp_r64(uint64_t n)
|
||||
{
|
||||
return (uint64_t) ngx_rtmp_r32(n) << 32 | ngx_rtmp_r32(n >> 32);
|
||||
return (uint64_t) ngx_rtmp_r32((uint32_t) n) << 32 |
|
||||
ngx_rtmp_r32((uint32_t) (n >> 32));
|
||||
}
|
||||
|
||||
|
||||
|
@ -445,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) \
|
||||
|
@ -455,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);
|
||||
|
@ -470,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,
|
||||
|
@ -547,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 */
|
||||
|
@ -555,14 +597,14 @@ ngx_int_t ngx_rtmp_send_sample_access(ngx_rtmp_session_t *s);
|
|||
#define NGX_RTMP_VIDEO_DISPOSABLE_FRAME 3
|
||||
|
||||
|
||||
static inline ngx_int_t
|
||||
static ngx_inline ngx_int_t
|
||||
ngx_rtmp_get_video_frame_type(ngx_chain_t *in)
|
||||
{
|
||||
return (in->buf->pos[0] & 0xf0) >> 4;
|
||||
}
|
||||
|
||||
|
||||
static inline ngx_int_t
|
||||
static ngx_inline ngx_int_t
|
||||
ngx_rtmp_is_codec_header(ngx_chain_t *in)
|
||||
{
|
||||
return in->buf->pos + 1 < in->buf->last && in->buf->pos[1] == 0;
|
||||
|
@ -573,6 +615,15 @@ extern ngx_rtmp_bandwidth_t ngx_rtmp_bw_out;
|
|||
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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
118
ngx_rtmp_amf.c
118
ngx_rtmp_amf.c
|
@ -1,12 +1,17 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include "ngx_rtmp_amf.h"
|
||||
#include "ngx_rtmp.h"
|
||||
#include <string.h>
|
||||
|
||||
static inline void*
|
||||
|
||||
static ngx_inline void*
|
||||
ngx_rtmp_amf_reverse_copy(void *dst, void* src, size_t len)
|
||||
{
|
||||
size_t k;
|
||||
|
@ -22,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
|
||||
|
@ -84,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
|
||||
|
@ -164,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;
|
||||
|
@ -181,13 +186,26 @@ ngx_rtmp_amf_read_object(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts,
|
|||
maxlen = namelen;
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
for( ;; ) {
|
||||
|
||||
#if !(NGX_WIN32)
|
||||
char name[maxlen];
|
||||
|
||||
/* read key */
|
||||
if (ngx_rtmp_amf_get(ctx, buf, 2) != NGX_OK)
|
||||
#else
|
||||
char name[1024];
|
||||
if (maxlen > sizeof(name)) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
#endif
|
||||
/* read key */
|
||||
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);
|
||||
|
||||
|
@ -209,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);
|
||||
|
@ -228,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;
|
||||
|
@ -251,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;
|
||||
|
@ -288,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;
|
||||
|
@ -310,15 +328,17 @@ 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 &&
|
||||
ngx_rtmp_amf_is_compatible_type(elts->type & 0xff, type))
|
||||
data = (elts &&
|
||||
ngx_rtmp_amf_is_compatible_type(
|
||||
(uint8_t) (elts->type & 0xff), (uint8_t) type))
|
||||
? elts->data
|
||||
: NULL;
|
||||
|
||||
|
@ -379,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;
|
||||
}
|
||||
|
@ -406,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;
|
||||
|
@ -443,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)
|
||||
{
|
||||
|
@ -453,11 +474,11 @@ ngx_rtmp_amf_write_object(ngx_rtmp_amf_ctx_t *ctx,
|
|||
|
||||
for(n = 0; n < nelts; ++n) {
|
||||
|
||||
len = elts[n].name.len;
|
||||
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;
|
||||
}
|
||||
|
@ -479,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)
|
||||
{
|
||||
|
@ -488,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;
|
||||
}
|
||||
|
@ -505,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)
|
||||
{
|
||||
|
@ -521,7 +542,7 @@ ngx_rtmp_amf_write(ngx_rtmp_amf_ctx_t *ctx,
|
|||
|
||||
type = elts[n].type;
|
||||
data = elts[n].data;
|
||||
len = elts[n].len;
|
||||
len = (uint16_t) elts[n].len;
|
||||
|
||||
if (type & NGX_RTMP_AMF_TYPELESS) {
|
||||
type &= ~NGX_RTMP_AMF_TYPELESS;
|
||||
|
@ -533,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;
|
||||
}
|
||||
|
@ -549,12 +570,12 @@ ngx_rtmp_amf_write(ngx_rtmp_amf_ctx_t *ctx,
|
|||
|
||||
case NGX_RTMP_AMF_STRING:
|
||||
if (len == 0 && data) {
|
||||
len = ngx_strlen((u_char*)data);
|
||||
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;
|
||||
}
|
||||
|
@ -573,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;
|
||||
|
@ -585,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;
|
||||
|
@ -600,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;
|
||||
}
|
||||
|
@ -609,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;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include "ngx_rtmp_cmd_module.h"
|
||||
#include "ngx_rtmp_relay_module.h"
|
||||
|
||||
|
@ -15,10 +18,12 @@ static ngx_int_t ngx_rtmp_auto_push_init_process(ngx_cycle_t *cycle);
|
|||
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);
|
||||
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,
|
||||
ngx_rtmp_delete_stream_t *v);
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
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,
|
||||
ngx_rtmp_delete_stream_t *v);
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct ngx_rtmp_auto_push_ctx_s ngx_rtmp_auto_push_ctx_t;
|
||||
|
@ -88,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"
|
||||
|
||||
|
||||
|
@ -97,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;
|
||||
|
@ -107,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;
|
||||
|
@ -149,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");
|
||||
|
@ -200,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;
|
||||
|
@ -223,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;
|
||||
|
||||
|
@ -242,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);
|
||||
|
@ -269,7 +302,7 @@ ngx_rtmp_auto_push_create_conf(ngx_cycle_t *cycle)
|
|||
}
|
||||
|
||||
apcf->auto_push = NGX_CONF_UNSET;
|
||||
apcf->push_reconnect = NGX_CONF_UNSET;
|
||||
apcf->push_reconnect = NGX_CONF_UNSET_MSEC;
|
||||
|
||||
return apcf;
|
||||
}
|
||||
|
@ -291,6 +324,7 @@ ngx_rtmp_auto_push_init_conf(ngx_cycle_t *cycle, void *conf)
|
|||
}
|
||||
|
||||
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
static void
|
||||
ngx_rtmp_auto_push_reconnect(ngx_event_t *ev)
|
||||
{
|
||||
|
@ -303,19 +337,22 @@ ngx_rtmp_auto_push_reconnect(ngx_event_t *ev)
|
|||
ngx_rtmp_relay_target_t at;
|
||||
u_char path[sizeof("unix:") + NGX_MAX_PATH];
|
||||
u_char flash_ver[sizeof("APSH ,") +
|
||||
NGX_OFF_T_LEN * 2];
|
||||
NGX_INT_T_LEN * 2];
|
||||
u_char play_path[NGX_RTMP_MAX_NAME];
|
||||
ngx_str_t name;
|
||||
u_char *p;
|
||||
ngx_str_t *u;
|
||||
ngx_pid_t pid;
|
||||
ngx_int_t npushed;
|
||||
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;
|
||||
}
|
||||
|
@ -335,6 +372,7 @@ ngx_rtmp_auto_push_reconnect(ngx_event_t *ev)
|
|||
}
|
||||
|
||||
slot = ctx->slots;
|
||||
npushed = 0;
|
||||
|
||||
for (n = 0; n < NGX_MAX_PROCESSES; ++n, ++slot) {
|
||||
if (n == ngx_process_slot) {
|
||||
|
@ -347,6 +385,7 @@ ngx_rtmp_auto_push_reconnect(ngx_event_t *ev)
|
|||
}
|
||||
|
||||
if (*slot) {
|
||||
npushed++;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -355,9 +394,18 @@ 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;
|
||||
|
||||
if (ngx_file_info(path + sizeof("unix:") - 1, &fi) != NGX_OK) {
|
||||
ngx_log_debug5(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"auto_push: " ngx_file_info_n " failed: "
|
||||
"slot=%i pid=%P socket='%s'" "url='%V' name='%s'",
|
||||
n, pid, path, u, ctx->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
u->data = path;
|
||||
u->len = p - path;
|
||||
if (ngx_parse_url(s->connection->pool, &at.url) != NGX_OK) {
|
||||
|
@ -368,29 +416,59 @@ 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,
|
||||
"auto_push: connect slot=%i pid=%i socket='%s' "
|
||||
"name='%s'",
|
||||
n, (ngx_int_t) pid, path, ctx->name);
|
||||
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);
|
||||
|
||||
if (ngx_rtmp_relay_push(s, &name, &at) == NGX_OK) {
|
||||
*slot = 1;
|
||||
npushed++;
|
||||
continue;
|
||||
}
|
||||
|
||||
ngx_log_debug5(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"auto_push: connect failed: slot=%i pid=%P socket='%s'"
|
||||
"url='%V' name='%s'",
|
||||
n, pid, path, u, ctx->name);
|
||||
}
|
||||
|
||||
ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
|
||||
ngx_core_module);
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"auto_push: pushed=%i total=%i failed=%i",
|
||||
npushed, ccf->worker_processes,
|
||||
ccf->worker_processes - 1 - npushed);
|
||||
|
||||
if (ccf->worker_processes == npushed + 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* several workers failed */
|
||||
|
||||
slot = ctx->slots;
|
||||
|
||||
for (n = 0; n < NGX_MAX_PROCESSES; ++n, ++slot) {
|
||||
pid = ngx_processes[n].pid;
|
||||
|
||||
if (n == ngx_process_slot || *slot == 1 ||
|
||||
pid == 0 || pid == NGX_INVALID_PID)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
|
||||
"auto_push: connect failed: slot=%i pid=%i socket='%s'"
|
||||
"url='%V' name='%s'",
|
||||
n, (ngx_int_t) pid, path, u, ctx->name);
|
||||
"auto_push: connect failed: slot=%i pid=%P name='%s'",
|
||||
n, pid, ctx->name);
|
||||
}
|
||||
|
||||
if (!ctx->push_evt.timer_set) {
|
||||
ngx_add_timer(&ctx->push_evt, apcf->push_reconnect);
|
||||
}
|
||||
if (!ctx->push_evt.timer_set) {
|
||||
ngx_add_timer(&ctx->push_evt, apcf->push_reconnect);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -401,24 +479,24 @@ ngx_rtmp_auto_push_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
|
|||
ngx_rtmp_auto_push_conf_t *apcf;
|
||||
ngx_rtmp_auto_push_ctx_t *ctx;
|
||||
|
||||
if (s->auto_pushed || s->relay) {
|
||||
if (s->auto_pushed || (s->relay && !s->static_relay)) {
|
||||
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));
|
||||
|
@ -427,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;
|
||||
|
@ -444,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;
|
||||
|
@ -452,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);
|
||||
|
@ -468,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;
|
||||
}
|
||||
|
@ -497,3 +575,4 @@ ngx_rtmp_auto_push_delete_stream(ngx_rtmp_session_t *s,
|
|||
next:
|
||||
return next_delete_stream(s, v);
|
||||
}
|
||||
#endif /* NGX_HAVE_UNIX_DOMAIN */
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#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;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
|
@ -7,11 +8,12 @@
|
|||
#define _NGX_RTMP_BANDWIDTH_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
|
||||
|
||||
/* Bandwidth update interval in seconds */
|
||||
#define NGX_RTMP_BANDWIDTH_INTERVAL 60
|
||||
#define NGX_RTMP_BANDWIDTH_INTERVAL 10
|
||||
|
||||
|
||||
typedef struct {
|
||||
|
@ -27,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
63
ngx_rtmp_bitop.c
Normal 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
46
ngx_rtmp_bitop.h
Normal 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_ */
|
|
@ -1,7 +1,11 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include "ngx_rtmp_cmd_module.h"
|
||||
#include "ngx_rtmp_streams.h"
|
||||
|
||||
|
@ -10,22 +14,56 @@
|
|||
#define NGX_RTMP_CAPABILITIES 31
|
||||
|
||||
|
||||
ngx_rtmp_connect_pt ngx_rtmp_connect;
|
||||
ngx_rtmp_create_stream_pt ngx_rtmp_create_stream;
|
||||
ngx_rtmp_close_stream_pt ngx_rtmp_close_stream;
|
||||
ngx_rtmp_delete_stream_pt ngx_rtmp_delete_stream;
|
||||
ngx_rtmp_publish_pt ngx_rtmp_publish;
|
||||
ngx_rtmp_play_pt ngx_rtmp_play;
|
||||
ngx_rtmp_seek_pt ngx_rtmp_seek;
|
||||
ngx_rtmp_pause_pt ngx_rtmp_pause;
|
||||
static ngx_int_t ngx_rtmp_cmd_connect(ngx_rtmp_session_t *s,
|
||||
ngx_rtmp_connect_t *v);
|
||||
static ngx_int_t ngx_rtmp_cmd_disconnect(ngx_rtmp_session_t *s);
|
||||
static ngx_int_t ngx_rtmp_cmd_create_stream(ngx_rtmp_session_t *s,
|
||||
ngx_rtmp_create_stream_t *v);
|
||||
static ngx_int_t ngx_rtmp_cmd_close_stream(ngx_rtmp_session_t *s,
|
||||
ngx_rtmp_close_stream_t *v);
|
||||
static ngx_int_t ngx_rtmp_cmd_delete_stream(ngx_rtmp_session_t *s,
|
||||
ngx_rtmp_delete_stream_t *v);
|
||||
static ngx_int_t ngx_rtmp_cmd_publish(ngx_rtmp_session_t *s,
|
||||
ngx_rtmp_publish_t *v);
|
||||
static ngx_int_t ngx_rtmp_cmd_play(ngx_rtmp_session_t *s,
|
||||
ngx_rtmp_play_t *v);
|
||||
static ngx_int_t ngx_rtmp_cmd_seek(ngx_rtmp_session_t *s,
|
||||
ngx_rtmp_seek_t *v);
|
||||
static ngx_int_t ngx_rtmp_cmd_pause(ngx_rtmp_session_t *s,
|
||||
ngx_rtmp_pause_t *v);
|
||||
|
||||
|
||||
ngx_rtmp_stream_begin_pt ngx_rtmp_stream_begin;
|
||||
ngx_rtmp_stream_eof_pt ngx_rtmp_stream_eof;
|
||||
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;
|
||||
static ngx_int_t ngx_rtmp_cmd_stream_begin(ngx_rtmp_session_t *s,
|
||||
ngx_rtmp_stream_begin_t *v);
|
||||
static ngx_int_t ngx_rtmp_cmd_stream_eof(ngx_rtmp_session_t *s,
|
||||
ngx_rtmp_stream_eof_t *v);
|
||||
static ngx_int_t ngx_rtmp_cmd_stream_dry(ngx_rtmp_session_t *s,
|
||||
ngx_rtmp_stream_dry_t *v);
|
||||
static ngx_int_t ngx_rtmp_cmd_recorded(ngx_rtmp_session_t *s,
|
||||
ngx_rtmp_recorded_t *v);
|
||||
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;
|
||||
ngx_rtmp_create_stream_pt ngx_rtmp_create_stream;
|
||||
ngx_rtmp_close_stream_pt ngx_rtmp_close_stream;
|
||||
ngx_rtmp_delete_stream_pt ngx_rtmp_delete_stream;
|
||||
ngx_rtmp_publish_pt ngx_rtmp_publish;
|
||||
ngx_rtmp_play_pt ngx_rtmp_play;
|
||||
ngx_rtmp_seek_pt ngx_rtmp_seek;
|
||||
ngx_rtmp_pause_pt ngx_rtmp_pause;
|
||||
|
||||
|
||||
ngx_rtmp_stream_begin_pt ngx_rtmp_stream_begin;
|
||||
ngx_rtmp_stream_eof_pt ngx_rtmp_stream_eof;
|
||||
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);
|
||||
|
||||
|
@ -58,6 +96,22 @@ ngx_module_t ngx_rtmp_cmd_module = {
|
|||
};
|
||||
|
||||
|
||||
void
|
||||
ngx_rtmp_cmd_fill_args(u_char name[NGX_RTMP_MAX_NAME],
|
||||
u_char args[NGX_RTMP_MAX_ARGS])
|
||||
{
|
||||
u_char *p;
|
||||
|
||||
p = (u_char *)ngx_strchr(name, '?');
|
||||
if (p == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
*p++ = 0;
|
||||
ngx_cpystrn(args, p, NGX_RTMP_MAX_ARGS);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_cmd_connect_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
||||
ngx_chain_t *in)
|
||||
|
@ -68,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) },
|
||||
|
||||
|
@ -113,17 +167,29 @@ 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;
|
||||
}
|
||||
|
||||
ngx_rtmp_cmd_fill_args(v.app, v.args);
|
||||
|
||||
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",
|
||||
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);
|
||||
|
||||
return ngx_rtmp_connect(s, &v);
|
||||
}
|
||||
|
||||
|
@ -143,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 },
|
||||
|
@ -154,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"),
|
||||
|
@ -174,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,
|
||||
|
@ -191,21 +257,13 @@ 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;
|
||||
}
|
||||
|
||||
cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
|
||||
|
||||
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
|
||||
"connect: app='%s' flashver='%s' swf_url='%s' "
|
||||
"tc_url='%s' page_url='%s' acodecs=%uD vcodecs=%uD "
|
||||
"object_encoding=%ui",
|
||||
v->app, 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);
|
||||
|
||||
trans = v->trans;
|
||||
|
||||
/* fill session parameters */
|
||||
|
@ -222,6 +280,7 @@ ngx_rtmp_cmd_connect(ngx_rtmp_session_t *s, ngx_rtmp_connect_t *v)
|
|||
ngx_memcpy(s->name.data, v->name, s->name.len)
|
||||
|
||||
NGX_RTMP_SET_STRPAR(app);
|
||||
NGX_RTMP_SET_STRPAR(args);
|
||||
NGX_RTMP_SET_STRPAR(flashver);
|
||||
NGX_RTMP_SET_STRPAR(swf_url);
|
||||
NGX_RTMP_SET_STRPAR(tc_url);
|
||||
|
@ -234,8 +293,8 @@ ngx_rtmp_cmd_connect(ngx_rtmp_session_t *s, ngx_rtmp_connect_t *v)
|
|||
s->app.len = (p - s->app.data);
|
||||
}
|
||||
|
||||
s->acodecs = v->acodecs;
|
||||
s->vcodecs = v->vcodecs;
|
||||
s->acodecs = (uint32_t) v->acodecs;
|
||||
s->vcodecs = (uint32_t) v->vcodecs;
|
||||
|
||||
/* find application & set app_conf */
|
||||
cacfp = cscf->applications.elts;
|
||||
|
@ -250,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;
|
||||
}
|
||||
|
@ -258,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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -275,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;
|
||||
}
|
||||
|
@ -296,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;
|
||||
|
||||
|
@ -310,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 },
|
||||
|
||||
|
@ -346,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;
|
||||
|
@ -373,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 },
|
||||
|
||||
|
@ -386,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;
|
||||
}
|
||||
|
@ -409,22 +468,6 @@ ngx_rtmp_cmd_delete_stream(ngx_rtmp_session_t *s, ngx_rtmp_delete_stream_t *v)
|
|||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_rtmp_cmd_fill_args(u_char name[NGX_RTMP_MAX_NAME],
|
||||
u_char args[NGX_RTMP_MAX_ARGS])
|
||||
{
|
||||
u_char *p;
|
||||
|
||||
p = (u_char *)ngx_strchr(name, '?');
|
||||
if (p == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
*p++ = 0;
|
||||
ngx_cpystrn(args, p, NGX_RTMP_MAX_ARGS);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_cmd_publish_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
||||
ngx_chain_t *in)
|
||||
|
@ -435,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,
|
||||
|
@ -453,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;
|
||||
}
|
||||
|
@ -484,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 },
|
||||
|
||||
|
@ -511,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;
|
||||
|
@ -522,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);
|
||||
|
@ -533,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)
|
||||
|
@ -545,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 },
|
||||
|
||||
|
@ -564,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;
|
||||
}
|
||||
|
@ -586,11 +694,18 @@ ngx_rtmp_cmd_pause(ngx_rtmp_session_t *s, ngx_rtmp_pause_t *v)
|
|||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_cmd_disconnect(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
||||
ngx_rtmp_cmd_disconnect_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
||||
ngx_chain_t *in)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "disconnect");
|
||||
|
||||
return ngx_rtmp_disconnect(s);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_cmd_disconnect(ngx_rtmp_session_t *s)
|
||||
{
|
||||
return ngx_rtmp_delete_stream(s, NULL);
|
||||
}
|
||||
|
||||
|
@ -604,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 },
|
||||
|
||||
|
@ -619,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;
|
||||
}
|
||||
|
@ -675,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 },
|
||||
|
@ -682,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 },
|
||||
|
@ -698,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 */
|
||||
|
||||
|
@ -707,7 +831,7 @@ ngx_rtmp_cmd_postconfiguration(ngx_conf_t *cf)
|
|||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*h = ngx_rtmp_cmd_disconnect;
|
||||
*h = ngx_rtmp_cmd_disconnect_init;
|
||||
|
||||
/* register AMF callbacks */
|
||||
|
||||
|
@ -724,11 +848,10 @@ ngx_rtmp_cmd_postconfiguration(ngx_conf_t *cf)
|
|||
*ch = *bh;
|
||||
}
|
||||
|
||||
/* set initial handlers */
|
||||
|
||||
ngx_rtmp_connect = ngx_rtmp_cmd_connect;
|
||||
ngx_rtmp_disconnect = ngx_rtmp_cmd_disconnect;
|
||||
ngx_rtmp_create_stream = ngx_rtmp_cmd_create_stream;
|
||||
ngx_rtmp_close_stream = ngx_rtmp_cmd_close_stream;
|
||||
ngx_rtmp_close_stream = ngx_rtmp_cmd_close_stream;
|
||||
ngx_rtmp_delete_stream = ngx_rtmp_cmd_delete_stream;
|
||||
ngx_rtmp_publish = ngx_rtmp_cmd_publish;
|
||||
ngx_rtmp_play = ngx_rtmp_cmd_play;
|
||||
|
@ -741,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;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
|
@ -13,9 +14,8 @@
|
|||
#include "ngx_rtmp.h"
|
||||
|
||||
|
||||
#define NGX_RTMP_MAX_APP 128
|
||||
#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
|
||||
|
||||
|
||||
|
@ -23,8 +23,9 @@
|
|||
|
||||
typedef struct {
|
||||
double trans;
|
||||
u_char app[NGX_RTMP_MAX_APP];
|
||||
u_char flashver[32];
|
||||
u_char app[NGX_RTMP_MAX_NAME];
|
||||
u_char args[NGX_RTMP_MAX_ARGS];
|
||||
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,21 +103,26 @@ 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,
|
||||
ngx_rtmp_create_stream_t *v);
|
||||
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,
|
||||
|
@ -124,8 +136,10 @@ 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;
|
||||
extern ngx_rtmp_create_stream_pt ngx_rtmp_create_stream;
|
||||
extern ngx_rtmp_close_stream_pt ngx_rtmp_close_stream;
|
||||
extern ngx_rtmp_delete_stream_pt ngx_rtmp_delete_stream;
|
||||
|
@ -140,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_ */
|
||||
|
|
|
@ -1,14 +1,65 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#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 = {
|
||||
|
@ -18,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 */
|
||||
|
@ -39,7 +90,7 @@ ngx_module_t ngx_rtmp_codec_module = {
|
|||
};
|
||||
|
||||
|
||||
static const char *
|
||||
static const char *
|
||||
audio_codecs[] = {
|
||||
"",
|
||||
"ADPCM",
|
||||
|
@ -61,7 +112,7 @@ audio_codecs[] = {
|
|||
};
|
||||
|
||||
|
||||
static const char *
|
||||
static const char *
|
||||
video_codecs[] = {
|
||||
"",
|
||||
"Jpeg",
|
||||
|
@ -74,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])
|
||||
|
@ -83,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])
|
||||
|
@ -107,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;
|
||||
|
@ -140,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));
|
||||
|
@ -179,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 {
|
||||
|
@ -198,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -273,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;
|
||||
|
@ -287,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;
|
||||
|
@ -298,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 },
|
||||
|
||||
|
@ -355,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) },
|
||||
};
|
||||
|
@ -381,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();
|
||||
|
@ -405,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;
|
||||
|
||||
|
@ -418,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;
|
||||
|
@ -451,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) },
|
||||
|
||||
|
@ -498,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));
|
||||
|
@ -515,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 = v.width;
|
||||
ctx->height = v.height;
|
||||
ctx->duration = v.duration;
|
||||
ctx->frame_rate = v.frame_rate;
|
||||
ctx->video_data_rate = v.video_data_rate;
|
||||
ctx->video_codec_id = v.video_codec_id_n;
|
||||
ctx->audio_data_rate = v.audio_data_rate;
|
||||
ctx->audio_codec_id = (v.audio_codec_id_n == -1
|
||||
? 0 : v.audio_codec_id_n == 0
|
||||
? NGX_RTMP_AUDIO_UNCOMPRESSED : 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.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);
|
||||
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)
|
||||
{
|
||||
|
@ -585,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;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
|
@ -7,6 +8,8 @@
|
|||
#define _NGX_RTMP_CODEC_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include "ngx_rtmp.h"
|
||||
|
||||
|
||||
|
@ -49,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 */
|
||||
|
|
|
@ -1,38 +1,47 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <nginx.h>
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
#include "ngx_rtmp.h"
|
||||
#include "ngx_rtmp_live_module.h"
|
||||
#include "ngx_rtmp_record_module.h"
|
||||
|
||||
|
||||
static ngx_int_t ngx_rtmp_control_postconfiguration(ngx_conf_t *cf);
|
||||
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,15 +53,16 @@ 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[] = {
|
||||
|
||||
{ ngx_string("rtmp_control"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
|
||||
ngx_conf_set_bitmask_slot,
|
||||
ngx_rtmp_control,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_rtmp_control_loc_conf_t, control),
|
||||
ngx_rtmp_control_masks },
|
||||
|
@ -62,351 +72,462 @@ static ngx_command_t ngx_rtmp_control_commands[] = {
|
|||
|
||||
|
||||
static ngx_http_module_t ngx_rtmp_control_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_rtmp_control_postconfiguration, /* 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;
|
||||
}
|
||||
|
||||
len = NGX_OFF_T_LEN;
|
||||
/* output count */
|
||||
|
||||
len = NGX_INT_T_LEN;
|
||||
|
||||
p = ngx_palloc(r->connection->pool, len);
|
||||
if (p == NULL) {
|
||||
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(§ion, sizeof(section));
|
||||
ngx_memzero(&method, sizeof(method));
|
||||
|
||||
ngx_str_null(§ion);
|
||||
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'",
|
||||
§ion, &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);
|
||||
|
||||
|
@ -511,19 +720,13 @@ ngx_rtmp_control_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
|||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_control_postconfiguration(ngx_conf_t *cf)
|
||||
static char *
|
||||
ngx_rtmp_control(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_handler_pt *h;
|
||||
ngx_http_core_main_conf_t *cmcf;
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
|
||||
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
|
||||
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
|
||||
clcf->handler = ngx_rtmp_control_handler;
|
||||
|
||||
h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
*h = ngx_rtmp_control_handler;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
return ngx_conf_set_bitmask_slot(cf, cmd, conf);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_event.h>
|
||||
#include <nginx.h>
|
||||
#include "ngx_rtmp.h"
|
||||
|
||||
|
||||
|
@ -29,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"
|
||||
};
|
||||
|
||||
|
@ -44,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,
|
||||
|
@ -149,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
|
||||
};
|
||||
|
||||
|
@ -233,12 +241,13 @@ ngx_rtmp_core_create_srv_conf(ngx_conf_t *cf)
|
|||
conf->so_keepalive = NGX_CONF_UNSET;
|
||||
conf->max_streams = NGX_CONF_UNSET;
|
||||
conf->chunk_size = NGX_CONF_UNSET;
|
||||
conf->ack_window = NGX_CONF_UNSET;
|
||||
conf->max_message = NGX_CONF_UNSET;
|
||||
conf->out_queue = NGX_CONF_UNSET;
|
||||
conf->out_cork = NGX_CONF_UNSET;
|
||||
conf->ack_window = NGX_CONF_UNSET_UINT;
|
||||
conf->max_message = NGX_CONF_UNSET_SIZE;
|
||||
conf->out_queue = NGX_CONF_UNSET_SIZE;
|
||||
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;
|
||||
|
@ -259,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) {
|
||||
|
@ -322,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;
|
||||
|
@ -347,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);
|
||||
|
@ -360,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) {
|
||||
|
@ -369,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -409,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;
|
||||
|
@ -428,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;
|
||||
}
|
||||
}
|
||||
|
@ -453,7 +474,7 @@ ngx_rtmp_core_application(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
|||
cscf = pctx->srv_conf[ngx_rtmp_core_module.ctx_index];
|
||||
|
||||
cacfp = ngx_array_push(&cscf->applications);
|
||||
if (cacf == NULL) {
|
||||
if (cacfp == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
|
@ -478,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;
|
||||
|
@ -535,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;
|
||||
}
|
||||
|
||||
|
@ -555,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) {
|
||||
|
@ -576,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;
|
||||
|
@ -599,7 +621,11 @@ ngx_rtmp_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
|||
ls->bind = 1;
|
||||
|
||||
} else {
|
||||
len = ngx_sock_ntop(sa, buf, NGX_SOCKADDR_STRLEN, 1);
|
||||
len = ngx_sock_ntop(sa,
|
||||
#if (nginx_version >= 1005003)
|
||||
ls->socklen,
|
||||
#endif
|
||||
buf, NGX_SOCKADDR_STRLEN, 1);
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"ipv6only is not supported "
|
||||
|
@ -705,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;
|
||||
|
|
|
@ -1,577 +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;
|
||||
char **args;
|
||||
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 = ec->args.elts;
|
||||
args[0] = (char *)ec->cmd.data;
|
||||
for (n = 0; n < ec->args.nelts; ++n, ++arg) {
|
||||
ngx_rtmp_eval(s, arg, ngx_rtmp_enotify_eval_p, &a);
|
||||
args[n + 1] = (char *) a.data;
|
||||
}
|
||||
args[n + 1] = 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;
|
||||
}
|
147
ngx_rtmp_eval.c
147
ngx_rtmp_eval.c
|
@ -1,8 +1,11 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include "ngx_rtmp_eval.h"
|
||||
|
||||
|
||||
|
@ -10,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);
|
||||
}
|
||||
|
||||
|
@ -56,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;
|
||||
}
|
||||
|
@ -79,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;
|
||||
|
@ -89,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -100,10 +102,10 @@ 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;;
|
||||
u_char c, *p;
|
||||
ngx_str_t name;
|
||||
ngx_buf_t b;
|
||||
ngx_uint_t n;
|
||||
|
@ -115,13 +117,13 @@ 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;
|
||||
}
|
||||
|
||||
b.end = b.pos + NGX_RTMP_EVAL_BUFLEN;
|
||||
name.data = NULL;
|
||||
|
||||
for (n = 0; n < in->len; ++n) {
|
||||
p = &in->data[n];
|
||||
|
@ -134,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;
|
||||
|
||||
|
@ -151,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) {
|
||||
|
@ -162,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;
|
||||
|
||||
|
@ -175,14 +182,106 @@ 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;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_rtmp_eval_streams(ngx_str_t *in)
|
||||
{
|
||||
#if !(NGX_WIN32)
|
||||
ngx_int_t mode, create, v, close_src;
|
||||
ngx_fd_t dst, src;
|
||||
u_char *path;
|
||||
|
||||
path = in->data;
|
||||
|
||||
while (*path >= '0' && *path <= '9') {
|
||||
path++;
|
||||
}
|
||||
|
||||
switch ((char) *path) {
|
||||
|
||||
case '>':
|
||||
|
||||
v = (path == in->data ? 1 : ngx_atoi(in->data, path - in->data));
|
||||
if (v == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
dst = (ngx_fd_t) v;
|
||||
mode = NGX_FILE_WRONLY;
|
||||
create = NGX_FILE_TRUNCATE;
|
||||
path++;
|
||||
|
||||
if (*path == (u_char) '>') {
|
||||
mode = NGX_FILE_APPEND;
|
||||
create = NGX_FILE_CREATE_OR_OPEN;
|
||||
path++;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '<':
|
||||
|
||||
v = (path == in->data ? 0 : ngx_atoi(in->data, path - in->data));
|
||||
if (v == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
dst = (ngx_fd_t) v;
|
||||
mode = NGX_FILE_RDONLY;
|
||||
create = NGX_FILE_OPEN;
|
||||
path++;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
return NGX_DONE;
|
||||
}
|
||||
|
||||
if (*path == (u_char) '&') {
|
||||
|
||||
path++;
|
||||
v = ngx_atoi(path, in->data + in->len - path);
|
||||
if (v == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
src = (ngx_fd_t) v;
|
||||
close_src = 0;
|
||||
|
||||
} else {
|
||||
|
||||
src = ngx_open_file(path, mode, create, NGX_FILE_DEFAULT_ACCESS);
|
||||
if (src == NGX_INVALID_FILE) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
close_src = 1;
|
||||
|
||||
}
|
||||
|
||||
if (src == dst) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
dup2(src, dst);
|
||||
|
||||
if (close_src) {
|
||||
ngx_close_file(src);
|
||||
}
|
||||
return NGX_OK;
|
||||
|
||||
#else
|
||||
return NGX_DONE;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1,18 +1,22 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_RTMP_EVAL_H_INCLUDED_
|
||||
#define _NGX_RTMP_EVAL_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include "ngx_rtmp.h"
|
||||
|
||||
|
||||
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);
|
||||
|
||||
|
||||
|
@ -30,8 +34,11 @@ 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);
|
||||
|
||||
|
||||
#endif /* _NGX_RTMP_EVAL_H_INCLUDED_ */
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,8 +1,11 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include "ngx_rtmp_play_module.h"
|
||||
#include "ngx_rtmp_codec_module.h"
|
||||
#include "ngx_rtmp_streams.h"
|
||||
|
@ -10,9 +13,10 @@
|
|||
|
||||
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);
|
||||
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);
|
||||
static ngx_int_t ngx_rtmp_flv_start(ngx_rtmp_session_t *s, ngx_file_t *f);
|
||||
static ngx_int_t ngx_rtmp_flv_seek(ngx_rtmp_session_t *s, ngx_file_t *f,
|
||||
ngx_uint_t offset);
|
||||
|
@ -43,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
|
||||
|
@ -118,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"),
|
||||
×_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) },
|
||||
};
|
||||
|
@ -157,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(×_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)
|
||||
{
|
||||
|
@ -207,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;
|
||||
|
@ -222,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) {
|
||||
|
@ -231,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;
|
||||
}
|
||||
|
@ -261,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) {
|
||||
|
@ -271,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;
|
||||
}
|
||||
|
@ -288,19 +291,19 @@ ngx_rtmp_flv_timestamp_to_offset(ngx_rtmp_session_t *s, ngx_file_t *f,
|
|||
goto rewind;
|
||||
}
|
||||
|
||||
ret = ngx_rtmp_flv_index_value(ngx_rtmp_flv_buffer);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -326,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)) {
|
||||
|
@ -363,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) {
|
||||
|
@ -422,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)) {
|
||||
|
@ -468,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;
|
||||
|
@ -476,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) {
|
||||
|
@ -502,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);
|
||||
|
@ -527,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;
|
||||
|
||||
|
@ -538,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;
|
||||
|
@ -548,7 +553,8 @@ next:
|
|||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_flv_init(ngx_rtmp_session_t *s, ngx_file_t *f)
|
||||
ngx_rtmp_flv_init(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_int_t aindex,
|
||||
ngx_int_t vindex)
|
||||
{
|
||||
ngx_rtmp_flv_ctx_t *ctx;
|
||||
|
||||
|
@ -647,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;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include "ngx_rtmp.h"
|
||||
#include "ngx_rtmp_amf.h"
|
||||
|
||||
|
@ -13,13 +16,16 @@ static void ngx_rtmp_ping(ngx_event_t *rev);
|
|||
static ngx_int_t ngx_rtmp_finalize_set_chunk_size(ngx_rtmp_session_t *s);
|
||||
|
||||
|
||||
ngx_uint_t ngx_rtmp_naccepted;
|
||||
|
||||
|
||||
ngx_rtmp_bandwidth_t ngx_rtmp_bw_out;
|
||||
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[] = {
|
||||
"?",
|
||||
|
@ -54,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",
|
||||
|
@ -93,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;
|
||||
}
|
||||
|
@ -138,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;
|
||||
|
@ -157,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;
|
||||
|
@ -235,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;
|
||||
|
@ -266,12 +274,19 @@ 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,
|
||||
"sending RTMP ACK(%D)", s->in_bytes);
|
||||
"sending RTMP ACK(%uD)", s->in_bytes);
|
||||
|
||||
if (ngx_rtmp_send_ack(s, s->in_bytes)) {
|
||||
ngx_rtmp_finalize_session(s);
|
||||
|
@ -342,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*)×tamp;
|
||||
pp[2] = *p++;
|
||||
|
@ -356,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;
|
||||
|
@ -426,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 */
|
||||
|
@ -489,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);
|
||||
|
@ -545,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;
|
||||
|
@ -576,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;
|
||||
|
@ -675,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 */
|
||||
|
@ -694,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;
|
||||
|
@ -705,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,
|
||||
|
@ -736,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)
|
||||
{
|
||||
|
@ -752,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
|
||||
|
@ -782,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,
|
||||
|
@ -797,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;
|
||||
|
@ -842,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) {
|
||||
|
@ -853,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) {
|
||||
|
@ -868,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;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include "ngx_rtmp.h"
|
||||
|
||||
#include <openssl/hmac.h>
|
||||
|
@ -31,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
|
||||
};
|
||||
|
||||
|
@ -68,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
|
||||
|
||||
|
||||
|
@ -86,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 };
|
||||
|
@ -98,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;
|
||||
}
|
||||
|
@ -157,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;
|
||||
|
@ -182,7 +192,7 @@ static void
|
|||
ngx_rtmp_fill_random_buffer(ngx_buf_t *b)
|
||||
{
|
||||
for (; b->last != b->end; ++b->last) {
|
||||
*b->last = rand();
|
||||
*b->last = (u_char) rand();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -267,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;
|
||||
|
@ -276,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;
|
||||
}
|
||||
|
@ -301,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;
|
||||
|
@ -311,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;
|
||||
|
@ -351,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;
|
||||
|
@ -378,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);
|
||||
|
@ -420,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;
|
||||
|
@ -451,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;
|
||||
|
@ -466,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;
|
||||
|
@ -493,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);
|
||||
|
@ -536,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;
|
||||
|
@ -600,16 +610,19 @@ 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;
|
||||
}
|
||||
|
||||
if (async) {
|
||||
ngx_post_event(c->write, &ngx_posted_events);
|
||||
ngx_add_timer(c->write, s->timeout);
|
||||
if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
|
||||
ngx_rtmp_finalize_session(s);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +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);
|
||||
|
@ -26,6 +30,7 @@ ngx_rtmp_init_connection(ngx_connection_t *c)
|
|||
ngx_rtmp_in6_addr_t *addr6;
|
||||
#endif
|
||||
|
||||
++ngx_rtmp_naccepted;
|
||||
|
||||
/* find the server configuration for the address:port */
|
||||
|
||||
|
@ -71,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;
|
||||
|
@ -105,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;
|
||||
|
@ -117,12 +123,19 @@ ngx_rtmp_init_connection(ngx_connection_t *c)
|
|||
c->number, &c->addr_text);
|
||||
|
||||
s = ngx_rtmp_init_session(c, addr_conf);
|
||||
if (s == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* only auto-pushed connections are
|
||||
* done through unix socket */
|
||||
|
||||
s->auto_pushed = unix_socket;
|
||||
|
||||
if (s) {
|
||||
if (addr_conf->proxy_protocol) {
|
||||
ngx_rtmp_proxy_protocol(s);
|
||||
|
||||
} else {
|
||||
ngx_rtmp_handshake(s);
|
||||
}
|
||||
}
|
||||
|
@ -135,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);
|
||||
|
@ -178,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);
|
||||
|
||||
|
||||
|
@ -239,6 +257,10 @@ ngx_rtmp_close_connection(ngx_connection_t *c)
|
|||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, c->log, 0, "close connection");
|
||||
|
||||
#if (NGX_STAT_STUB)
|
||||
(void) ngx_atomic_fetch_add(ngx_stat_active, -1);
|
||||
#endif
|
||||
|
||||
pool = c->pool;
|
||||
ngx_close_connection(c);
|
||||
ngx_destroy_pool(pool);
|
||||
|
@ -259,27 +281,25 @@ ngx_rtmp_close_session_handler(ngx_event_t *e)
|
|||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, c->log, 0, "close session");
|
||||
|
||||
if (s) {
|
||||
ngx_rtmp_fire_event(s, NGX_RTMP_DISCONNECT, NULL, NULL);
|
||||
ngx_rtmp_fire_event(s, NGX_RTMP_DISCONNECT, NULL, NULL);
|
||||
|
||||
if (s->ping_evt.timer_set) {
|
||||
ngx_del_timer(&s->ping_evt);
|
||||
}
|
||||
if (s->ping_evt.timer_set) {
|
||||
ngx_del_timer(&s->ping_evt);
|
||||
}
|
||||
|
||||
if (s->in_old_pool) {
|
||||
ngx_destroy_pool(s->in_old_pool);
|
||||
}
|
||||
if (s->in_old_pool) {
|
||||
ngx_destroy_pool(s->in_old_pool);
|
||||
}
|
||||
|
||||
if (s->in_pool) {
|
||||
ngx_destroy_pool(s->in_pool);
|
||||
}
|
||||
if (s->in_pool) {
|
||||
ngx_destroy_pool(s->in_pool);
|
||||
}
|
||||
|
||||
ngx_rtmp_free_handshake_buffers(s);
|
||||
ngx_rtmp_free_handshake_buffers(s);
|
||||
|
||||
while (s->out_pos != s->out_last) {
|
||||
ngx_rtmp_free_shared_chain(cscf, s->out[s->out_pos++]);
|
||||
s->out_pos %= s->out_queue;
|
||||
}
|
||||
while (s->out_pos != s->out_last) {
|
||||
ngx_rtmp_free_shared_chain(cscf, s->out[s->out_pos++]);
|
||||
s->out_pos %= s->out_queue;
|
||||
}
|
||||
|
||||
ngx_rtmp_close_connection(c);
|
||||
|
|
205
ngx_rtmp_limit_module.c
Normal file
205
ngx_rtmp_limit_module.c
Normal file
|
@ -0,0 +1,205 @@
|
|||
|
||||
/*
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include "ngx_rtmp.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_int_t max_conn;
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
} ngx_rtmp_limit_main_conf_t;
|
||||
|
||||
|
||||
static ngx_str_t shm_name = ngx_string("rtmp_limit");
|
||||
|
||||
|
||||
static ngx_int_t ngx_rtmp_limit_postconfiguration(ngx_conf_t *cf);
|
||||
static void *ngx_rtmp_limit_create_main_conf(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_rtmp_limit_commands[] = {
|
||||
|
||||
{ ngx_string("max_connections"),
|
||||
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_num_slot,
|
||||
NGX_RTMP_MAIN_CONF_OFFSET,
|
||||
offsetof(ngx_rtmp_limit_main_conf_t, max_conn),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_rtmp_module_t ngx_rtmp_limit_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_rtmp_limit_postconfiguration, /* postconfiguration */
|
||||
ngx_rtmp_limit_create_main_conf, /* 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_limit_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_rtmp_limit_module_ctx, /* module context */
|
||||
ngx_rtmp_limit_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_limit_create_main_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_rtmp_limit_main_conf_t *lmcf;
|
||||
|
||||
lmcf = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_limit_main_conf_t));
|
||||
if (lmcf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lmcf->max_conn = NGX_CONF_UNSET;
|
||||
|
||||
return lmcf;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
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;
|
||||
ngx_slab_pool_t *shpool;
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
uint32_t *nconn, n;
|
||||
ngx_int_t rc;
|
||||
|
||||
lmcf = ngx_rtmp_get_module_main_conf(s, ngx_rtmp_limit_module);
|
||||
if (lmcf->max_conn == NGX_CONF_UNSET) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
shm_zone = lmcf->shm_zone;
|
||||
shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
|
||||
nconn = shm_zone->data;
|
||||
|
||||
ngx_shmtx_lock(&shpool->mutex);
|
||||
n = ++*nconn;
|
||||
ngx_shmtx_unlock(&shpool->mutex);
|
||||
|
||||
rc = n > (ngx_uint_t) lmcf->max_conn ? NGX_ERROR : NGX_OK;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"limit: inc conection counter: %uD", n);
|
||||
|
||||
if (rc != NGX_OK) {
|
||||
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
|
||||
"limit: too many connections: %uD > %i",
|
||||
n, lmcf->max_conn);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
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;
|
||||
ngx_slab_pool_t *shpool;
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
uint32_t *nconn, n;
|
||||
|
||||
lmcf = ngx_rtmp_get_module_main_conf(s, ngx_rtmp_limit_module);
|
||||
if (lmcf->max_conn == NGX_CONF_UNSET) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
shm_zone = lmcf->shm_zone;
|
||||
shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
|
||||
nconn = shm_zone->data;
|
||||
|
||||
ngx_shmtx_lock(&shpool->mutex);
|
||||
n = --*nconn;
|
||||
ngx_shmtx_unlock(&shpool->mutex);
|
||||
|
||||
(void) n;
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"limit: dec conection counter: %uD", n);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_limit_shm_init(ngx_shm_zone_t *shm_zone, void *data)
|
||||
{
|
||||
ngx_slab_pool_t *shpool;
|
||||
uint32_t *nconn;
|
||||
|
||||
if (data) {
|
||||
shm_zone->data = data;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
|
||||
|
||||
nconn = ngx_slab_alloc(shpool, 4);
|
||||
if (nconn == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*nconn = 0;
|
||||
|
||||
shm_zone->data = nconn;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_limit_postconfiguration(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_rtmp_core_main_conf_t *cmcf;
|
||||
ngx_rtmp_limit_main_conf_t *lmcf;
|
||||
ngx_rtmp_handler_pt *h;
|
||||
|
||||
cmcf = ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_core_module);
|
||||
|
||||
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;
|
||||
|
||||
lmcf = ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_limit_module);
|
||||
if (lmcf->max_conn == NGX_CONF_UNSET) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
lmcf->shm_zone = ngx_shared_memory_add(cf, &shm_name, ngx_pagesize * 2,
|
||||
&ngx_rtmp_limit_module);
|
||||
if (lmcf->shm_zone == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
lmcf->shm_zone->init = ngx_rtmp_limit_shm_init;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include "ngx_rtmp_live_module.h"
|
||||
#include "ngx_rtmp_cmd_module.h"
|
||||
#include "ngx_rtmp_codec_module.h"
|
||||
|
@ -18,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);
|
||||
|
@ -35,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"),
|
||||
|
@ -98,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,
|
||||
|
@ -148,16 +151,16 @@ 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;
|
||||
lacf->sync = NGX_CONF_UNSET;
|
||||
lacf->idle_timeout = NGX_CONF_UNSET;
|
||||
lacf->buffer = NGX_CONF_UNSET;
|
||||
lacf->sync = NGX_CONF_UNSET_MSEC;
|
||||
lacf->idle_timeout = NGX_CONF_UNSET_MSEC;
|
||||
lacf->interleave = NGX_CONF_UNSET;
|
||||
lacf->wait_key = NGX_CONF_UNSET;
|
||||
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;
|
||||
}
|
||||
|
@ -170,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, 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, 1);
|
||||
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;
|
||||
|
@ -250,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;
|
||||
|
||||
|
@ -258,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;
|
||||
|
@ -354,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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -377,10 +383,9 @@ ngx_rtmp_live_start(ngx_rtmp_session_t *s)
|
|||
if (lacf->play_restart) {
|
||||
status[nstatus++] = ngx_rtmp_create_status(s, "NetStream.Play.Start",
|
||||
"status", "Start live");
|
||||
status[nstatus++] = ngx_rtmp_create_sample_access(s);
|
||||
}
|
||||
|
||||
status[nstatus++] = ngx_rtmp_create_sample_access(s);
|
||||
|
||||
if (lacf->publish_notify) {
|
||||
status[nstatus++] = ngx_rtmp_create_status(s,
|
||||
"NetStream.Play.PublishNotify",
|
||||
|
@ -424,7 +429,7 @@ ngx_rtmp_live_stop(ngx_rtmp_session_t *s)
|
|||
if (lacf->publish_notify) {
|
||||
status[nstatus++] = ngx_rtmp_create_status(s,
|
||||
"NetStream.Play.UnpublishNotify",
|
||||
"status", "Start publishing");
|
||||
"status", "Stop publishing");
|
||||
}
|
||||
|
||||
ngx_rtmp_live_set_status(s, control, status, nstatus, 0);
|
||||
|
@ -509,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;
|
||||
}
|
||||
|
||||
|
@ -537,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_AUDIO;
|
||||
ctx->cs[1].csid = NGX_RTMP_CSID_VIDEO;
|
||||
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);
|
||||
|
@ -553,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;
|
||||
|
||||
|
@ -591,6 +609,21 @@ ngx_rtmp_live_close_stream(ngx_rtmp_session_t *s, ngx_rtmp_close_stream_t *v)
|
|||
ngx_rtmp_live_stop(s);
|
||||
}
|
||||
|
||||
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) {
|
||||
ctx->stream = NULL;
|
||||
goto next;
|
||||
|
@ -664,18 +697,18 @@ 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;
|
||||
ngx_rtmp_codec_ctx_t *codec_ctx;
|
||||
ngx_chain_t *header, *coheader, *meta,
|
||||
*apkt, *acopkt, *rpkt;
|
||||
*apkt, *aapkt, *acopkt, *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, lh, clh;
|
||||
ngx_int_t rc, mandatory;
|
||||
ngx_int_t rc, mandatory, dummy_audio;
|
||||
ngx_uint_t prio;
|
||||
ngx_uint_t peers;
|
||||
ngx_uint_t meta_version;
|
||||
|
@ -720,8 +753,11 @@ 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;
|
||||
acopkt = NULL;
|
||||
header = NULL;
|
||||
coheader = NULL;
|
||||
|
@ -751,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);
|
||||
|
||||
|
@ -759,7 +795,7 @@ ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
|||
cs->timestamp = ch.timestamp;
|
||||
|
||||
delta = ch.timestamp - lh.timestamp;
|
||||
|
||||
/*
|
||||
if (delta >> 31) {
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"live: clipping non-monotonical timestamp %uD->%uD",
|
||||
|
@ -769,7 +805,7 @@ ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
|||
|
||||
ch.timestamp = lh.timestamp;
|
||||
}
|
||||
|
||||
*/
|
||||
rpkt = ngx_rtmp_append_shared_bufs(cscf, NULL, in);
|
||||
|
||||
ngx_rtmp_prepare_message(s, &ch, &lh, rpkt);
|
||||
|
@ -807,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;
|
||||
}
|
||||
|
@ -870,6 +906,17 @@ ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
|||
continue;
|
||||
}
|
||||
|
||||
dummy_audio = 0;
|
||||
if (lacf->wait_video && h->type == NGX_RTMP_MSG_VIDEO &&
|
||||
!pctx->cs[1].active)
|
||||
{
|
||||
dummy_audio = 1;
|
||||
if (aapkt == NULL) {
|
||||
aapkt = ngx_rtmp_alloc_shared_buf(cscf);
|
||||
ngx_rtmp_prepare_message(s, &clh, NULL, aapkt);
|
||||
}
|
||||
}
|
||||
|
||||
if (header || coheader) {
|
||||
|
||||
/* send absolute codec header */
|
||||
|
@ -900,10 +947,14 @@ ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
|||
if (rc != NGX_OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
} else if (dummy_audio) {
|
||||
ngx_rtmp_send_message(ss, aapkt, 0);
|
||||
}
|
||||
|
||||
cs->timestamp = lh.timestamp;
|
||||
cs->active = 1;
|
||||
ss->current_time = cs->timestamp;
|
||||
|
||||
} else {
|
||||
|
||||
|
@ -925,9 +976,14 @@ 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;
|
||||
|
||||
if (dummy_audio) {
|
||||
ngx_rtmp_send_message(ss, aapkt, 0);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -954,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) {
|
||||
|
@ -964,6 +1021,10 @@ ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
|||
ngx_rtmp_free_shared_chain(cscf, apkt);
|
||||
}
|
||||
|
||||
if (aapkt) {
|
||||
ngx_rtmp_free_shared_chain(cscf, aapkt);
|
||||
}
|
||||
|
||||
if (acopkt) {
|
||||
ngx_rtmp_free_shared_chain(cscf, acopkt);
|
||||
}
|
||||
|
@ -971,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)
|
||||
|
@ -1015,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;
|
||||
|
||||
|
@ -1026,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 */
|
||||
|
@ -1043,9 +1457,12 @@ ngx_rtmp_live_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
|
|||
if (!ctx->silent && !lacf->play_restart) {
|
||||
ngx_rtmp_send_status(s, "NetStream.Play.Start",
|
||||
"status", "Start live");
|
||||
ngx_rtmp_send_sample_access(s);
|
||||
}
|
||||
|
||||
next:
|
||||
ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
|
||||
"live: ngx_rtmp_live_play: next");
|
||||
return next_play(s, v);
|
||||
}
|
||||
|
||||
|
@ -1055,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);
|
||||
|
||||
|
@ -1086,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;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
|
@ -7,6 +8,8 @@
|
|||
#define _NGX_RTMP_LIVE_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include "ngx_rtmp.h"
|
||||
#include "ngx_rtmp_cmd_module.h"
|
||||
#include "ngx_rtmp_bandwidth.h"
|
||||
|
@ -30,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;
|
||||
|
@ -45,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;
|
||||
|
@ -65,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;
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2013 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include "ngx_rtmp_cmd_module.h"
|
||||
|
||||
|
||||
|
@ -13,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);
|
||||
|
@ -156,16 +159,16 @@ ngx_rtmp_log_var_default_getdata(ngx_rtmp_session_t *s, u_char *buf,
|
|||
|
||||
|
||||
static size_t
|
||||
ngx_rtmp_log_var_number_getlen(ngx_rtmp_session_t *s, ngx_rtmp_log_op_t *op)
|
||||
ngx_rtmp_log_var_connection_getlen(ngx_rtmp_session_t *s, ngx_rtmp_log_op_t *op)
|
||||
{
|
||||
return NGX_OFF_T_LEN;
|
||||
return NGX_INT_T_LEN;
|
||||
}
|
||||
|
||||
static u_char *
|
||||
ngx_rtmp_log_var_connection_getdata(ngx_rtmp_session_t *s, u_char *buf,
|
||||
ngx_rtmp_log_op_t *op)
|
||||
{
|
||||
return ngx_sprintf(buf, "%O", (off_t) s->connection->number);
|
||||
return ngx_sprintf(buf, "%ui", (ngx_uint_t) s->connection->number);
|
||||
}
|
||||
|
||||
|
||||
|
@ -186,11 +189,31 @@ 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)
|
||||
{
|
||||
return ((ngx_str_t *) ((uint8_t *) s + op->offset))->len;
|
||||
return ((ngx_str_t *) ((u_char *) s + op->offset))->len;
|
||||
}
|
||||
|
||||
|
||||
|
@ -200,7 +223,7 @@ ngx_rtmp_log_var_session_string_getdata(ngx_rtmp_session_t *s, u_char *buf,
|
|||
{
|
||||
ngx_str_t *str;
|
||||
|
||||
str = (ngx_str_t *) ((uint8_t *) s + op->offset);
|
||||
str = (ngx_str_t *) ((u_char *) s + op->offset);
|
||||
|
||||
return ngx_cpymem(buf, str->data, str->len);
|
||||
}
|
||||
|
@ -301,7 +324,7 @@ ngx_rtmp_log_var_time_local_getdata(ngx_rtmp_session_t *s, u_char *buf,
|
|||
ngx_rtmp_log_op_t *op)
|
||||
{
|
||||
return ngx_cpymem(buf, ngx_cached_http_log_time.data,
|
||||
ngx_cached_http_log_time.len);
|
||||
ngx_cached_http_log_time.len);
|
||||
}
|
||||
|
||||
|
||||
|
@ -326,7 +349,7 @@ static size_t
|
|||
ngx_rtmp_log_var_session_readable_time_getlen(ngx_rtmp_session_t *s,
|
||||
ngx_rtmp_log_op_t *op)
|
||||
{
|
||||
return NGX_OFF_T_LEN + sizeof("d 23h 59m 59s") - 1;
|
||||
return NGX_INT_T_LEN + sizeof("d 23h 59m 59s") - 1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -364,7 +387,7 @@ ngx_rtmp_log_var_session_readable_time_getdata(ngx_rtmp_session_t *s,
|
|||
|
||||
static ngx_rtmp_log_var_t ngx_rtmp_log_vars[] = {
|
||||
{ ngx_string("connection"),
|
||||
ngx_rtmp_log_var_number_getlen,
|
||||
ngx_rtmp_log_var_connection_getlen,
|
||||
ngx_rtmp_log_var_connection_getdata,
|
||||
0 },
|
||||
|
||||
|
@ -428,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,
|
||||
|
@ -825,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) {
|
||||
|
@ -839,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);
|
||||
}
|
||||
|
||||
|
@ -886,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;
|
||||
|
||||
|
@ -906,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;
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include "ngx_rtmp_play_module.h"
|
||||
#include "ngx_rtmp_codec_module.h"
|
||||
#include "ngx_rtmp_streams.h"
|
||||
|
||||
|
||||
static ngx_int_t ngx_rtmp_mp4_postconfiguration(ngx_conf_t *cf);
|
||||
static ngx_int_t ngx_rtmp_mp4_init(ngx_rtmp_session_t *s, ngx_file_t *f);
|
||||
static ngx_int_t ngx_rtmp_mp4_init(ngx_rtmp_session_t *s, ngx_file_t *f,
|
||||
ngx_int_t aindex, ngx_int_t vindex);
|
||||
static ngx_int_t ngx_rtmp_mp4_done(ngx_rtmp_session_t *s, ngx_file_t *f);
|
||||
static ngx_int_t ngx_rtmp_mp4_start(ngx_rtmp_session_t *s, ngx_file_t *f);
|
||||
static ngx_int_t ngx_rtmp_mp4_seek(ngx_rtmp_session_t *s, ngx_file_t *f,
|
||||
|
@ -20,9 +24,20 @@ 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)
|
||||
|
||||
|
||||
/* disable zero-sized array warning by msvc */
|
||||
|
||||
#if (NGX_WIN32)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4200)
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint32_t first_chunk;
|
||||
uint32_t samples_per_chunk;
|
||||
|
@ -99,6 +114,12 @@ typedef struct {
|
|||
uint64_t entries[0];
|
||||
} ngx_rtmp_mp4_offsets64_t;
|
||||
|
||||
|
||||
#if (NGX_WIN32)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
|
@ -160,6 +181,7 @@ typedef struct {
|
|||
typedef struct {
|
||||
void *mmaped;
|
||||
size_t mmaped_size;
|
||||
ngx_fd_t extra;
|
||||
|
||||
unsigned meta_sent:1;
|
||||
|
||||
|
@ -173,6 +195,9 @@ typedef struct {
|
|||
ngx_uint_t sample_size;
|
||||
ngx_uint_t sample_rate;
|
||||
|
||||
ngx_int_t atracks, vtracks;
|
||||
ngx_int_t aindex, vindex;
|
||||
|
||||
uint32_t start_timestamp, epoch;
|
||||
} ngx_rtmp_mp4_ctx_t;
|
||||
|
||||
|
@ -181,27 +206,100 @@ typedef struct {
|
|||
((uint32_t)d << 24 | (uint32_t)c << 16 | (uint32_t)b << 8 | (uint32_t)a)
|
||||
|
||||
|
||||
static inline uint32_t
|
||||
ngx_rtmp_mp4_to_rtmp_timestamp(ngx_rtmp_mp4_track_t *t, uint32_t ts)
|
||||
static ngx_inline uint32_t
|
||||
ngx_rtmp_mp4_to_rtmp_timestamp(ngx_rtmp_mp4_track_t *t, uint64_t ts)
|
||||
{
|
||||
return (uint64_t) ts * 1000 / t->time_scale;
|
||||
return (uint32_t) (ts * 1000 / t->time_scale);
|
||||
}
|
||||
|
||||
|
||||
static inline uint32_t
|
||||
static ngx_inline uint32_t
|
||||
ngx_rtmp_mp4_from_rtmp_timestamp(ngx_rtmp_mp4_track_t *t, uint32_t ts)
|
||||
{
|
||||
return (uint64_t) ts * t->time_scale / 1000;
|
||||
}
|
||||
|
||||
|
||||
#define NGX_RTMP_MP4_DEFAULT_BUFLEN 1000
|
||||
#define NGX_RTMP_MP4_BUFLEN_ADDON 1000
|
||||
|
||||
|
||||
static u_char ngx_rtmp_mp4_buffer[1024*1024];
|
||||
|
||||
|
||||
#if (NGX_WIN32)
|
||||
static void *
|
||||
ngx_rtmp_mp4_mmap(ngx_fd_t fd, size_t size, off_t offset, ngx_fd_t *extra)
|
||||
{
|
||||
void *data;
|
||||
|
||||
*extra = CreateFileMapping(fd, NULL, PAGE_READONLY,
|
||||
(DWORD) ((uint64_t) size >> 32),
|
||||
(DWORD) (size & 0xffffffff),
|
||||
NULL);
|
||||
if (*extra == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = MapViewOfFile(*extra, FILE_MAP_READ,
|
||||
(DWORD) ((uint64_t) offset >> 32),
|
||||
(DWORD) (offset & 0xffffffff),
|
||||
size);
|
||||
|
||||
if (data == NULL) {
|
||||
CloseHandle(*extra);
|
||||
}
|
||||
|
||||
/*
|
||||
* non-NULL result means map view handle is open
|
||||
* and should be closed later
|
||||
*/
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_mp4_munmap(void *data, size_t size, ngx_fd_t *extra)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
|
||||
rc = NGX_OK;
|
||||
|
||||
if (UnmapViewOfFile(data) == 0) {
|
||||
rc = NGX_ERROR;
|
||||
}
|
||||
|
||||
if (CloseHandle(*extra) == 0) {
|
||||
rc = NGX_ERROR;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void *
|
||||
ngx_rtmp_mp4_mmap(ngx_fd_t fd, size_t size, off_t offset, ngx_fd_t *extra)
|
||||
{
|
||||
void *data;
|
||||
|
||||
data = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, offset);
|
||||
|
||||
/* valid address is never NULL since there's no MAP_FIXED */
|
||||
|
||||
return data == MAP_FAILED ? NULL : data;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_mp4_munmap(void *data, size_t size, ngx_fd_t *extra)
|
||||
{
|
||||
return munmap(data, size);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static ngx_int_t ngx_rtmp_mp4_parse(ngx_rtmp_session_t *s, u_char *pos,
|
||||
u_char *last);
|
||||
static ngx_int_t ngx_rtmp_mp4_parse_trak(ngx_rtmp_session_t *s, u_char *pos,
|
||||
|
@ -228,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);
|
||||
|
||||
|
||||
|
@ -261,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 },
|
||||
|
@ -271,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 {
|
||||
|
@ -351,7 +450,7 @@ ngx_rtmp_mp4_parse_trak(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
|
|||
? NULL : &ctx->tracks[ctx->ntracks];
|
||||
|
||||
if (ctx->track) {
|
||||
ngx_memzero(ctx->track, sizeof(ctx->track));
|
||||
ngx_memzero(ctx->track, sizeof(*ctx->track));
|
||||
ctx->track->id = ctx->ntracks;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
|
@ -363,11 +462,31 @@ 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,
|
||||
"mp4: adding track %ui", ctx->ntracks);
|
||||
|
||||
if (ctx->track->type == NGX_RTMP_MSG_AUDIO) {
|
||||
if (ctx->atracks++ != ctx->aindex) {
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"mp4: skipping audio track %ui!=%ui",
|
||||
ctx->atracks - 1, ctx->aindex);
|
||||
ctx->track = NULL;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (ctx->vtracks++ != ctx->vindex) {
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"mp4: skipping video track %i!=%i",
|
||||
ctx->vtracks - 1, ctx->vindex);
|
||||
ctx->track = NULL;
|
||||
return NGX_OK;
|
||||
}
|
||||
}
|
||||
|
||||
++ctx->ntracks;
|
||||
|
||||
} else {
|
||||
|
@ -478,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;
|
||||
|
@ -512,8 +631,8 @@ 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 = ctx->track->codec;
|
||||
|
||||
ctx->track->fhdr = (u_char) ctx->track->codec;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
@ -525,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);
|
||||
|
||||
|
@ -538,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);
|
||||
|
||||
|
@ -581,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;
|
||||
|
@ -707,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);
|
||||
|
@ -764,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) {
|
||||
|
@ -801,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) {
|
||||
|
@ -880,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;
|
||||
}
|
||||
|
@ -911,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;
|
||||
}
|
||||
|
@ -942,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;
|
||||
}
|
||||
|
@ -973,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;
|
||||
}
|
||||
|
@ -1006,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;
|
||||
}
|
||||
|
@ -1017,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;
|
||||
}
|
||||
|
@ -1048,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;
|
||||
}
|
||||
|
@ -1080,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;
|
||||
}
|
||||
|
@ -1111,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;
|
||||
}
|
||||
|
@ -1142,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;
|
||||
}
|
||||
|
@ -1151,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);
|
||||
|
@ -1198,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),
|
||||
|
@ -1217,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;
|
||||
|
@ -1234,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;
|
||||
|
@ -1266,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),
|
||||
|
@ -1304,12 +1442,12 @@ ngx_rtmp_mp4_update_offset(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
|
|||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cr->offset = ngx_rtmp_r32(t->offsets->entries[chunk]);
|
||||
cr->offset = (off_t) ngx_rtmp_r32(t->offsets->entries[chunk]);
|
||||
cr->size = 0;
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"mp4: track#%ui offset[%ui/%uD]=%O",
|
||||
t->id, cr->chunk,
|
||||
t->id, cr->chunk,
|
||||
ngx_rtmp_r32(t->offsets->entry_count),
|
||||
cr->offset);
|
||||
|
||||
|
@ -1326,7 +1464,7 @@ ngx_rtmp_mp4_update_offset(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
|
|||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cr->offset = ngx_rtmp_r32(t->offsets64->entries[chunk]);
|
||||
cr->offset = (off_t) ngx_rtmp_r64(t->offsets64->entries[chunk]);
|
||||
cr->size = 0;
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
|
@ -1363,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++;
|
||||
|
@ -1422,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);
|
||||
|
||||
|
@ -1444,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,
|
||||
|
@ -1494,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);
|
||||
|
||||
|
@ -1563,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);
|
||||
|
||||
|
@ -1616,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));
|
||||
|
||||
|
@ -1755,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);
|
||||
|
@ -1828,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) },
|
||||
};
|
||||
|
@ -1904,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;
|
||||
|
@ -1956,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);
|
||||
|
||||
|
@ -1984,40 +2123,65 @@ 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));
|
||||
|
||||
h.msid = NGX_RTMP_MSID;
|
||||
h.type = t->type;
|
||||
h.type = (uint8_t) t->type;
|
||||
h.csid = t->csid;
|
||||
|
||||
lh = h;
|
||||
|
@ -2035,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;
|
||||
|
@ -2065,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;
|
||||
|
@ -2089,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;
|
||||
|
@ -2107,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;
|
||||
|
@ -2126,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);
|
||||
|
@ -2135,45 +2297,26 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_mp4_init(ngx_rtmp_session_t *s, ngx_file_t *f)
|
||||
ngx_rtmp_mp4_init(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_int_t aindex,
|
||||
ngx_int_t vindex)
|
||||
{
|
||||
ngx_rtmp_mp4_ctx_t *ctx;
|
||||
uint32_t hdr[2];
|
||||
ssize_t n;
|
||||
size_t offset, page_offset, size;
|
||||
size_t offset, page_offset, size, shift;
|
||||
uint64_t extended_size;
|
||||
ngx_file_info_t fi;
|
||||
|
||||
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_mp4_module);
|
||||
|
||||
|
@ -2189,6 +2332,9 @@ ngx_rtmp_mp4_init(ngx_rtmp_session_t *s, ngx_file_t *f)
|
|||
|
||||
ngx_memzero(ctx, sizeof(*ctx));
|
||||
|
||||
ctx->aindex = aindex;
|
||||
ctx->vindex = vindex;
|
||||
|
||||
offset = 0;
|
||||
size = 0;
|
||||
|
||||
|
@ -2202,7 +2348,31 @@ ngx_rtmp_mp4_init(ngx_rtmp_session_t *s, ngx_file_t *f)
|
|||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
size = ngx_rtmp_r32(hdr[0]);
|
||||
size = (size_t) ngx_rtmp_r32(hdr[0]);
|
||||
shift = sizeof(hdr);
|
||||
|
||||
if (size == 1) {
|
||||
n = ngx_read_file(f, (u_char *) &extended_size,
|
||||
sizeof(extended_size), offset + sizeof(hdr));
|
||||
|
||||
if (n != sizeof(extended_size)) {
|
||||
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
|
||||
"mp4: error reading file at offset=%uz "
|
||||
"while searching for moov box", offset + 8);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
size = (size_t) ngx_rtmp_r64(extended_size);
|
||||
shift += sizeof(extended_size);
|
||||
|
||||
} else if (size == 0) {
|
||||
if (ngx_fd_info(f->fd, &fi) == NGX_FILE_ERROR) {
|
||||
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
|
||||
"mp4: " ngx_fd_info_n " failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
size = ngx_file_size(&fi) - offset;
|
||||
}
|
||||
|
||||
if (hdr[1] == ngx_rtmp_mp4_make_tag('m','o','o','v')) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
|
@ -2216,28 +2386,26 @@ ngx_rtmp_mp4_init(ngx_rtmp_session_t *s, ngx_file_t *f)
|
|||
offset += size;
|
||||
}
|
||||
|
||||
if (size < 8) {
|
||||
if (size < shift) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
size -= 8;
|
||||
offset += 8;
|
||||
size -= shift;
|
||||
offset += shift;
|
||||
|
||||
page_offset = offset & (ngx_pagesize - 1);
|
||||
ctx->mmaped_size = page_offset + size;
|
||||
|
||||
ctx->mmaped = mmap(NULL, ctx->mmaped_size, PROT_READ, MAP_SHARED,
|
||||
f->fd, offset - page_offset);
|
||||
|
||||
if (ctx->mmaped == MAP_FAILED) {
|
||||
ctx->mmaped = NULL;
|
||||
ctx->mmaped = ngx_rtmp_mp4_mmap(f->fd, ctx->mmaped_size,
|
||||
offset - page_offset, &ctx->extra);
|
||||
if (ctx->mmaped == NULL) {
|
||||
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
|
||||
"mp4: mmap failed at offset=%ui, size=%uz",
|
||||
offset, size);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -2253,7 +2421,9 @@ ngx_rtmp_mp4_done(ngx_rtmp_session_t *s, ngx_file_t *f)
|
|||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (munmap(ctx->mmaped, ctx->mmaped_size)) {
|
||||
if (ngx_rtmp_mp4_munmap(ctx->mmaped, ctx->mmaped_size, &ctx->extra)
|
||||
!= NGX_OK)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
|
||||
"mp4: munmap failed");
|
||||
return NGX_ERROR;
|
||||
|
@ -2398,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;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
|
@ -9,9 +10,9 @@
|
|||
|
||||
|
||||
static ngx_int_t ngx_rtmp_netcall_postconfiguration(ngx_conf_t *cf);
|
||||
static void * ngx_rtmp_netcall_create_app_conf(ngx_conf_t *cf);
|
||||
static char * ngx_rtmp_netcall_merge_app_conf(ngx_conf_t *cf,
|
||||
void *parent, void *child);
|
||||
static void * ngx_rtmp_netcall_create_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);
|
||||
static void ngx_rtmp_netcall_detach(ngx_connection_t *cc);
|
||||
|
@ -24,7 +25,7 @@ typedef struct {
|
|||
ngx_msec_t timeout;
|
||||
size_t bufsize;
|
||||
ngx_log_t *log;
|
||||
} ngx_rtmp_netcall_app_conf_t;
|
||||
} ngx_rtmp_netcall_srv_conf_t;
|
||||
|
||||
|
||||
typedef struct ngx_rtmp_netcall_session_s {
|
||||
|
@ -55,15 +56,15 @@ static ngx_command_t ngx_rtmp_netcall_commands[] = {
|
|||
{ ngx_string("netcall_timeout"),
|
||||
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_msec_slot,
|
||||
NGX_RTMP_APP_CONF_OFFSET,
|
||||
offsetof(ngx_rtmp_netcall_app_conf_t, timeout),
|
||||
NGX_RTMP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_rtmp_netcall_srv_conf_t, timeout),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("netcall_buffer"),
|
||||
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_size_slot,
|
||||
NGX_RTMP_APP_CONF_OFFSET,
|
||||
offsetof(ngx_rtmp_netcall_app_conf_t, bufsize),
|
||||
NGX_RTMP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_rtmp_netcall_srv_conf_t, bufsize),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
|
@ -75,10 +76,10 @@ static ngx_rtmp_module_t ngx_rtmp_netcall_module_ctx = {
|
|||
ngx_rtmp_netcall_postconfiguration, /* postconfiguration */
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
ngx_rtmp_netcall_create_app_conf, /* create app configuration */
|
||||
ngx_rtmp_netcall_merge_app_conf /* merge app configuration */
|
||||
ngx_rtmp_netcall_create_srv_conf, /* create server configuration */
|
||||
ngx_rtmp_netcall_merge_srv_conf, /* merge server configuration */
|
||||
NULL, /* create app configuration */
|
||||
NULL /* merge app configuration */
|
||||
};
|
||||
|
||||
|
||||
|
@ -99,29 +100,29 @@ ngx_module_t ngx_rtmp_netcall_module = {
|
|||
|
||||
|
||||
static void *
|
||||
ngx_rtmp_netcall_create_app_conf(ngx_conf_t *cf)
|
||||
ngx_rtmp_netcall_create_srv_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_rtmp_netcall_app_conf_t *nacf;
|
||||
ngx_rtmp_netcall_srv_conf_t *nscf;
|
||||
|
||||
nacf = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_netcall_app_conf_t));
|
||||
if (nacf == NULL) {
|
||||
nscf = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_netcall_srv_conf_t));
|
||||
if (nscf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nacf->timeout = NGX_CONF_UNSET_MSEC;
|
||||
nacf->bufsize = NGX_CONF_UNSET_SIZE;
|
||||
nscf->timeout = NGX_CONF_UNSET_MSEC;
|
||||
nscf->bufsize = NGX_CONF_UNSET_SIZE;
|
||||
|
||||
nacf->log = &cf->cycle->new_log;
|
||||
nscf->log = &cf->cycle->new_log;
|
||||
|
||||
return nacf;
|
||||
return nscf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_rtmp_netcall_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
ngx_rtmp_netcall_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_rtmp_netcall_app_conf_t *prev = parent;
|
||||
ngx_rtmp_netcall_app_conf_t *conf = child;
|
||||
ngx_rtmp_netcall_srv_conf_t *prev = parent;
|
||||
ngx_rtmp_netcall_srv_conf_t *conf = child;
|
||||
|
||||
ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 10000);
|
||||
ngx_conf_merge_size_value(conf->bufsize, prev->bufsize, 1024);
|
||||
|
@ -131,7 +132,7 @@ ngx_rtmp_netcall_merge_app_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)
|
||||
{
|
||||
|
@ -175,7 +176,7 @@ ngx_rtmp_netcall_create(ngx_rtmp_session_t *s, ngx_rtmp_netcall_init_t *ci)
|
|||
ngx_rtmp_netcall_ctx_t *ctx;
|
||||
ngx_peer_connection_t *pc;
|
||||
ngx_rtmp_netcall_session_t *cs;
|
||||
ngx_rtmp_netcall_app_conf_t *cacf;
|
||||
ngx_rtmp_netcall_srv_conf_t *nscf;
|
||||
ngx_connection_t *c, *cc;
|
||||
ngx_pool_t *pool;
|
||||
ngx_int_t rc;
|
||||
|
@ -183,15 +184,15 @@ ngx_rtmp_netcall_create(ngx_rtmp_session_t *s, ngx_rtmp_netcall_init_t *ci)
|
|||
pool = NULL;
|
||||
c = s->connection;
|
||||
|
||||
cacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_netcall_module);
|
||||
if (cacf == NULL) {
|
||||
nscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_netcall_module);
|
||||
if (nscf == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
@ -203,7 +204,7 @@ ngx_rtmp_netcall_create(ngx_rtmp_session_t *s, ngx_rtmp_netcall_init_t *ci)
|
|||
* Note we use shared (app-wide) log because
|
||||
* s->connection->log might be unavailable
|
||||
* in detached netcall when it's being closed */
|
||||
pool = ngx_create_pool(4096, cacf->log);
|
||||
pool = ngx_create_pool(4096, nscf->log);
|
||||
if (pool == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
@ -227,8 +228,8 @@ ngx_rtmp_netcall_create(ngx_rtmp_session_t *s, ngx_rtmp_netcall_init_t *ci)
|
|||
ngx_memcpy(cs->arg, ci->arg, ci->argsize);
|
||||
}
|
||||
|
||||
cs->timeout = cacf->timeout;
|
||||
cs->bufsize = cacf->bufsize;
|
||||
cs->timeout = nscf->timeout;
|
||||
cs->bufsize = nscf->bufsize;
|
||||
cs->url = ci->url;
|
||||
cs->session = s;
|
||||
cs->filter = ci->filter;
|
||||
|
@ -238,7 +239,7 @@ ngx_rtmp_netcall_create(ngx_rtmp_session_t *s, ngx_rtmp_netcall_init_t *ci)
|
|||
cs->detached = 1;
|
||||
}
|
||||
|
||||
pc->log = cacf->log;
|
||||
pc->log = nscf->log;
|
||||
pc->get = ngx_rtmp_netcall_get_peer;
|
||||
pc->free = ngx_rtmp_netcall_free_peer;
|
||||
pc->data = cs;
|
||||
|
@ -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,9 +546,9 @@ 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_OFF_T_LEN);
|
||||
content_type->len + NGX_SIZE_T_LEN);
|
||||
if (b == NULL) {
|
||||
return 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;
|
||||
|
@ -685,12 +716,9 @@ ngx_rtmp_netcall_memcache_set(ngx_rtmp_session_t *s, ngx_pool_t *pool,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
b = ngx_create_temp_buf(pool,
|
||||
sizeof("set ") - 1 + key->len +
|
||||
(sizeof(" ") - 1 + NGX_OFF_T_LEN) * 3 +
|
||||
(sizeof("\r\n") - 1) * 2
|
||||
+ value->len
|
||||
);
|
||||
b = ngx_create_temp_buf(pool, sizeof("set ") - 1 + key->len +
|
||||
(1 + NGX_INT_T_LEN) * 3 +
|
||||
(sizeof("\r\n") - 1) * 2 + value->len);
|
||||
|
||||
if (b == NULL) {
|
||||
return NULL;
|
||||
|
@ -699,9 +727,8 @@ ngx_rtmp_netcall_memcache_set(ngx_rtmp_session_t *s, ngx_pool_t *pool,
|
|||
cl->next = NULL;
|
||||
cl->buf = b;
|
||||
|
||||
b->last = ngx_sprintf(b->pos,
|
||||
"set %V %ui %ui %ui\r\n%V\r\n",
|
||||
key, flags, sec, (ngx_uint_t)value->len, value);
|
||||
b->last = ngx_sprintf(b->pos, "set %V %ui %ui %ui\r\n%V\r\n",
|
||||
key, flags, sec, (ngx_uint_t) value->len, value);
|
||||
|
||||
return cl;
|
||||
}
|
||||
|
|
|
@ -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
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,6 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
|
@ -7,16 +8,19 @@
|
|||
#define _NGX_RTMP_PLAY_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include "ngx_rtmp.h"
|
||||
#include "ngx_rtmp_cmd_module.h"
|
||||
|
||||
|
||||
typedef ngx_int_t (*ngx_rtmp_play_init_pt) (ngx_rtmp_session_t *s,
|
||||
ngx_file_t *f);
|
||||
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);
|
||||
|
@ -38,20 +42,43 @@ typedef struct {
|
|||
} ngx_rtmp_play_fmt_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
typedef struct ngx_rtmp_play_ctx_s ngx_rtmp_play_ctx_t;
|
||||
|
||||
|
||||
struct ngx_rtmp_play_ctx_s {
|
||||
ngx_rtmp_session_t *session;
|
||||
ngx_file_t file;
|
||||
ngx_rtmp_play_fmt_t *fmt;
|
||||
ngx_event_t send_evt;
|
||||
unsigned playing:1;
|
||||
unsigned opened:1;
|
||||
unsigned joined:1;
|
||||
ngx_uint_t ncrs;
|
||||
ngx_str_t name;
|
||||
} ngx_rtmp_play_ctx_t;
|
||||
ngx_uint_t nheader;
|
||||
ngx_uint_t nbody;
|
||||
size_t pfx_size;
|
||||
ngx_str_t sfx;
|
||||
ngx_uint_t file_id;
|
||||
ngx_int_t aindex, vindex;
|
||||
ngx_uint_t nentry;
|
||||
ngx_uint_t post_seek;
|
||||
u_char name[NGX_RTMP_MAX_NAME];
|
||||
ngx_rtmp_play_ctx_t *next;
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t root;
|
||||
ngx_str_t temp_path;
|
||||
ngx_str_t *root;
|
||||
ngx_url_t *url;
|
||||
} ngx_rtmp_play_entry_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t temp_path;
|
||||
ngx_str_t local_path;
|
||||
ngx_array_t entries; /* ngx_rtmp_play_entry_t * */
|
||||
ngx_uint_t nbuckets;
|
||||
ngx_rtmp_play_ctx_t **ctx;
|
||||
} ngx_rtmp_play_app_conf_t;
|
||||
|
||||
|
||||
|
|
197
ngx_rtmp_proxy_protocol.c
Normal file
197
ngx_rtmp_proxy_protocol.c
Normal 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
19
ngx_rtmp_proxy_protocol.h
Normal 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_ */
|
|
@ -1,20 +1,23 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include "ngx_rtmp.h"
|
||||
#include "ngx_rtmp_amf.h"
|
||||
#include "ngx_rtmp_cmd_module.h"
|
||||
#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;
|
||||
|
||||
|
@ -64,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
|
||||
|
@ -80,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;
|
||||
|
||||
|
@ -104,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;
|
||||
|
@ -189,7 +192,6 @@ ngx_rtmp_user_message_handler(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
|||
|
||||
return ngx_rtmp_recorded(s, &v);
|
||||
}
|
||||
break;
|
||||
|
||||
case NGX_RTMP_USER_PING_REQUEST:
|
||||
return ngx_rtmp_send_ping_response(s, val);
|
||||
|
@ -208,8 +210,6 @@ ngx_rtmp_user_message_handler(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
|||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
|
@ -256,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)
|
||||
{
|
||||
|
@ -322,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;
|
||||
}
|
||||
|
@ -364,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)
|
||||
{
|
||||
|
@ -379,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 {
|
||||
|
@ -396,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);
|
||||
|
@ -411,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");
|
||||
|
@ -425,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:
|
||||
|
@ -461,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
|
@ -89,6 +95,14 @@ static ngx_command_t ngx_rtmp_record_commands[] = {
|
|||
offsetof(ngx_rtmp_record_app_conf_t, unique),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("record_append"),
|
||||
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|
|
||||
NGX_RTMP_REC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_RTMP_APP_CONF_OFFSET,
|
||||
offsetof(ngx_rtmp_record_app_conf_t, append),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("record_lock"),
|
||||
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|
|
||||
NGX_RTMP_REC_CONF|NGX_CONF_TAKE1,
|
||||
|
@ -97,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,
|
||||
|
@ -180,13 +202,15 @@ ngx_rtmp_record_create_app_conf(ngx_conf_t *cf)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
racf->max_size = NGX_CONF_UNSET;
|
||||
racf->max_frames = NGX_CONF_UNSET;
|
||||
racf->interval = NGX_CONF_UNSET;
|
||||
racf->unique = NGX_CONF_UNSET;
|
||||
racf->lock_file = NGX_CONF_UNSET;
|
||||
racf->notify = NGX_CONF_UNSET;
|
||||
racf->url = NGX_CONF_UNSET_PTR;
|
||||
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;
|
||||
racf->append = NGX_CONF_UNSET;
|
||||
racf->lock_file = NGX_CONF_UNSET;
|
||||
racf->notify = NGX_CONF_UNSET;
|
||||
racf->url = NGX_CONF_UNSET_PTR;
|
||||
|
||||
if (ngx_array_init(&racf->rec, cf->pool, 1, sizeof(void *)) != NGX_OK) {
|
||||
return NULL;
|
||||
|
@ -206,11 +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);
|
||||
|
@ -237,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,
|
||||
|
@ -281,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);
|
||||
|
@ -309,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);
|
||||
|
@ -361,6 +387,7 @@ ngx_rtmp_record_make_path(ngx_rtmp_session_t *s,
|
|||
ngx_rtmp_record_ctx_t *ctx;
|
||||
ngx_rtmp_record_app_conf_t *rracf;
|
||||
u_char *p, *l;
|
||||
struct tm tm;
|
||||
|
||||
static u_char buf[NGX_TIME_T_LEN + 1];
|
||||
static u_char pbuf[NGX_MAX_PATH + 1];
|
||||
|
@ -373,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),
|
||||
|
@ -381,18 +408,23 @@ 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));
|
||||
}
|
||||
|
||||
p = ngx_cpymem(p, rracf->suffix.data,
|
||||
ngx_min(rracf->suffix.len, (size_t)(l - p)));
|
||||
*p = 0;
|
||||
if (ngx_strchr(rracf->suffix.data, '%')) {
|
||||
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,
|
||||
ngx_min(rracf->suffix.len, (size_t)(l - p)));
|
||||
}
|
||||
|
||||
*p = 0;
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -415,20 +447,25 @@ 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;
|
||||
ngx_err_t err;
|
||||
ngx_str_t path;
|
||||
ngx_int_t mode, create_mode;
|
||||
u_char buf[8], *p;
|
||||
off_t file_size;
|
||||
uint32_t tag_size, mlen, timestamp;
|
||||
|
||||
rracf = rctx->conf;
|
||||
tag_size = 0;
|
||||
|
||||
if (rctx->file.fd != NGX_INVALID_FILE) {
|
||||
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));
|
||||
|
@ -438,11 +475,15 @@ ngx_rtmp_record_node_open(ngx_rtmp_session_t *s,
|
|||
|
||||
ngx_rtmp_record_make_path(s, rctx, &path);
|
||||
|
||||
mode = rracf->append ? NGX_FILE_RDWR : NGX_FILE_WRONLY;
|
||||
create_mode = rracf->append ? NGX_FILE_CREATE_OR_OPEN : NGX_FILE_TRUNCATE;
|
||||
|
||||
ngx_memzero(&rctx->file, sizeof(rctx->file));
|
||||
rctx->file.offset = 0;
|
||||
rctx->file.log = s->connection->log;
|
||||
rctx->file.fd = ngx_open_file(path.data, NGX_FILE_WRONLY, NGX_FILE_TRUNCATE,
|
||||
rctx->file.fd = ngx_open_file(path.data, mode, create_mode,
|
||||
NGX_FILE_DEFAULT_ACCESS);
|
||||
ngx_str_set(&rctx->file.name, "recorded");
|
||||
|
||||
if (rctx->file.fd == NGX_INVALID_FILE) {
|
||||
err = ngx_errno;
|
||||
|
@ -458,6 +499,7 @@ ngx_rtmp_record_node_open(ngx_rtmp_session_t *s,
|
|||
return NGX_OK;
|
||||
}
|
||||
|
||||
#if !(NGX_WIN32)
|
||||
if (rracf->lock_file) {
|
||||
err = ngx_lock_fd(rctx->file.fd);
|
||||
if (err) {
|
||||
|
@ -465,8 +507,9 @@ ngx_rtmp_record_node_open(ngx_rtmp_session_t *s,
|
|||
"record: %V lock failed", &rracf->id);
|
||||
}
|
||||
}
|
||||
#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) {
|
||||
|
@ -474,6 +517,86 @@ ngx_rtmp_record_node_open(ngx_rtmp_session_t *s,
|
|||
rracf->id.data ? (char *) rracf->id.data : "");
|
||||
}
|
||||
|
||||
if (rracf->append) {
|
||||
|
||||
file_size = 0;
|
||||
timestamp = 0;
|
||||
|
||||
#if (NGX_WIN32)
|
||||
{
|
||||
LONG lo, hi;
|
||||
|
||||
lo = 0;
|
||||
hi = 0;
|
||||
lo = SetFilePointer(rctx->file.fd, lo, &hi, FILE_END);
|
||||
file_size = (lo == INVALID_SET_FILE_POINTER ?
|
||||
(off_t) -1 : (off_t) hi << 32 | (off_t) lo);
|
||||
}
|
||||
#else
|
||||
file_size = lseek(rctx->file.fd, 0, SEEK_END);
|
||||
#endif
|
||||
if (file_size == (off_t) -1) {
|
||||
ngx_log_error(NGX_LOG_CRIT, s->connection->log, ngx_errno,
|
||||
"record: %V seek failed", &rracf->id);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (file_size < 4) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (ngx_read_file(&rctx->file, buf, 4, file_size - 4) != 4) {
|
||||
ngx_log_error(NGX_LOG_CRIT, s->connection->log, ngx_errno,
|
||||
"record: %V tag size read failed", &rracf->id);
|
||||
goto done;
|
||||
}
|
||||
|
||||
p = (u_char *) &tag_size;
|
||||
p[0] = buf[3];
|
||||
p[1] = buf[2];
|
||||
p[2] = buf[1];
|
||||
p[3] = buf[0];
|
||||
|
||||
if (tag_size == 0 || tag_size + 4 > file_size) {
|
||||
file_size = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (ngx_read_file(&rctx->file, buf, 8, file_size - tag_size - 4) != 8)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_CRIT, s->connection->log, ngx_errno,
|
||||
"record: %V tag read failed", &rracf->id);
|
||||
goto done;
|
||||
}
|
||||
|
||||
p = (u_char *) &mlen;
|
||||
p[0] = buf[3];
|
||||
p[1] = buf[2];
|
||||
p[2] = buf[1];
|
||||
p[3] = 0;
|
||||
|
||||
if (tag_size != mlen + 11) {
|
||||
ngx_log_error(NGX_LOG_CRIT, s->connection->log, ngx_errno,
|
||||
"record: %V tag size mismatch: "
|
||||
"tag_size=%uD, mlen=%uD", &rracf->id, tag_size, mlen);
|
||||
goto done;
|
||||
}
|
||||
|
||||
p = (u_char *) ×tamp;
|
||||
p[3] = buf[7];
|
||||
p[0] = buf[6];
|
||||
p[1] = buf[5];
|
||||
p[2] = buf[4];
|
||||
|
||||
done:
|
||||
rctx->file.offset = file_size;
|
||||
rctx->time_shift = timestamp;
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"record: append offset=%O, time=%uD, tag_size=%uD",
|
||||
file_size, timestamp, tag_size);
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
@ -612,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);
|
||||
|
||||
|
@ -624,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;
|
||||
|
@ -682,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;
|
||||
|
||||
|
@ -689,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,
|
||||
|
@ -699,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) {
|
||||
|
@ -716,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;
|
||||
|
@ -725,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);
|
||||
|
@ -743,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)
|
||||
|
@ -758,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;
|
||||
|
||||
|
@ -783,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);
|
||||
|
||||
|
@ -803,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)
|
||||
{
|
||||
|
@ -820,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;
|
||||
}
|
||||
|
@ -854,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;
|
||||
|
@ -870,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;
|
||||
|
@ -878,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;
|
||||
|
@ -902,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)
|
||||
|
@ -940,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))
|
||||
|
@ -947,27 +1103,38 @@ ngx_rtmp_record_node_av(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx,
|
|||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (rctx->file.offset == 0) {
|
||||
rctx->epoch = h->timestamp;
|
||||
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_rtmp_record_write_header(&rctx->file) != NGX_OK) {
|
||||
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;
|
||||
|
||||
if (rctx->file.offset == 0 &&
|
||||
ngx_rtmp_record_write_header(&rctx->file) != NGX_OK)
|
||||
{
|
||||
ngx_rtmp_record_node_close(s, rctx);
|
||||
return NGX_OK;
|
||||
}
|
||||
#if 0
|
||||
/* metadata */
|
||||
if (codec_ctx->meta) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"record: writing metadata");
|
||||
ch.type = NGX_RTMP_MSG_AMF_META;
|
||||
ch.mlen = ngx_rtmp_record_get_chain_mlen(codec_ctx->meta);
|
||||
if (ngx_rtmp_record_write_frame(s, &ch, codec_ctx->meta)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_OK;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
|
||||
|
@ -978,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;
|
||||
|
@ -986,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;
|
||||
}
|
||||
|
@ -995,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;
|
||||
|
@ -1007,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;
|
||||
}
|
||||
|
@ -1020,20 +1187,20 @@ 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;
|
||||
}
|
||||
|
||||
if (ngx_rtmp_get_video_frame_type(in) == NGX_RTMP_VIDEO_KEY_FRAME &&
|
||||
(codec_ctx->video_codec_id != NGX_RTMP_VIDEO_H264 ||
|
||||
((codec_ctx && codec_ctx->video_codec_id != NGX_RTMP_VIDEO_H264) ||
|
||||
!ngx_rtmp_is_codec_header(in)))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
@ -1042,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;
|
||||
}
|
||||
|
@ -1052,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)
|
||||
{
|
||||
|
@ -1066,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;
|
||||
|
@ -1092,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;
|
||||
}
|
||||
}
|
||||
|
@ -1146,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;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
|
@ -7,25 +8,29 @@
|
|||
#define _NGX_RTMP_RECORD_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include "ngx_rtmp.h"
|
||||
|
||||
|
||||
#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;
|
||||
ngx_flag_t unique;
|
||||
ngx_flag_t append;
|
||||
ngx_flag_t lock_file;
|
||||
ngx_flag_t notify;
|
||||
ngx_url_t *url;
|
||||
|
@ -39,13 +44,17 @@ typedef struct {
|
|||
ngx_rtmp_record_app_conf_t *conf;
|
||||
ngx_file_t file;
|
||||
ngx_uint_t nframes;
|
||||
uint32_t epoch;
|
||||
uint32_t epoch, time_shift;
|
||||
ngx_time_t last;
|
||||
time_t timestamp;
|
||||
unsigned failed:1;
|
||||
unsigned initialized:1;
|
||||
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;
|
||||
|
||||
|
||||
|
@ -64,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);
|
||||
|
@ -76,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;
|
||||
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,12 +1,15 @@
|
|||
|
||||
/*
|
||||
* 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>
|
||||
#include "ngx_rtmp.h"
|
||||
|
||||
|
||||
|
@ -23,8 +26,9 @@ typedef struct {
|
|||
ngx_int_t start;
|
||||
ngx_int_t stop;
|
||||
|
||||
void *tag; /* usually module reference */
|
||||
void *data; /* module-specific data */
|
||||
void *tag; /* usually module reference */
|
||||
void *data; /* module-specific data */
|
||||
ngx_uint_t counter; /* mutable connection counter */
|
||||
} ngx_rtmp_relay_target_t;
|
||||
|
||||
|
||||
|
@ -38,7 +42,6 @@ struct ngx_rtmp_relay_ctx_s {
|
|||
ngx_rtmp_relay_ctx_t *publish;
|
||||
ngx_rtmp_relay_ctx_t *play;
|
||||
ngx_rtmp_relay_ctx_t *next;
|
||||
unsigned relay:1;
|
||||
|
||||
ngx_str_t app;
|
||||
ngx_str_t tc_url;
|
||||
|
@ -51,6 +54,7 @@ struct ngx_rtmp_relay_ctx_s {
|
|||
ngx_int_t stop;
|
||||
|
||||
ngx_event_t push_evt;
|
||||
ngx_event_t *static_evt;
|
||||
void *tag;
|
||||
void *data;
|
||||
};
|
||||
|
|
511
ngx_rtmp_send.c
511
ngx_rtmp_send.c
|
@ -1,8 +1,10 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include "ngx_rtmp.h"
|
||||
#include "ngx_rtmp_amf.h"
|
||||
#include "ngx_rtmp_streams.h"
|
||||
|
@ -23,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); \
|
||||
|
@ -72,11 +74,13 @@ ngx_rtmp_create_chunk_size(ngx_rtmp_session_t *s, uint32_t chunk_size)
|
|||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"chunk_size=%uD", chunk_size);
|
||||
|
||||
NGX_RTMP_USER_START(s, NGX_RTMP_MSG_CHUNK_SIZE);
|
||||
{
|
||||
NGX_RTMP_USER_START(s, NGX_RTMP_MSG_CHUNK_SIZE);
|
||||
|
||||
NGX_RTMP_USER_OUT4(chunk_size);
|
||||
NGX_RTMP_USER_OUT4(chunk_size);
|
||||
|
||||
NGX_RTMP_USER_END(s);
|
||||
NGX_RTMP_USER_END(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -94,11 +98,13 @@ ngx_rtmp_create_abort(ngx_rtmp_session_t *s, uint32_t csid)
|
|||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"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);
|
||||
NGX_RTMP_USER_OUT4(csid);
|
||||
|
||||
NGX_RTMP_USER_END(s);
|
||||
NGX_RTMP_USER_END(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -116,11 +122,13 @@ ngx_rtmp_create_ack(ngx_rtmp_session_t *s, uint32_t seq)
|
|||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"create: ack seq=%uD", seq);
|
||||
|
||||
NGX_RTMP_USER_START(s, NGX_RTMP_MSG_ACK);
|
||||
{
|
||||
NGX_RTMP_USER_START(s, NGX_RTMP_MSG_ACK);
|
||||
|
||||
NGX_RTMP_USER_OUT4(seq);
|
||||
NGX_RTMP_USER_OUT4(seq);
|
||||
|
||||
NGX_RTMP_USER_END(s);
|
||||
NGX_RTMP_USER_END(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -138,11 +146,13 @@ ngx_rtmp_create_ack_size(ngx_rtmp_session_t *s, uint32_t ack_size)
|
|||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"create: ack_size=%uD", ack_size);
|
||||
|
||||
NGX_RTMP_USER_START(s, NGX_RTMP_MSG_ACK_SIZE);
|
||||
{
|
||||
NGX_RTMP_USER_START(s, NGX_RTMP_MSG_ACK_SIZE);
|
||||
|
||||
NGX_RTMP_USER_OUT4(ack_size);
|
||||
NGX_RTMP_USER_OUT4(ack_size);
|
||||
|
||||
NGX_RTMP_USER_END(s);
|
||||
NGX_RTMP_USER_END(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -159,15 +169,17 @@ 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);
|
||||
|
||||
NGX_RTMP_USER_START(s, NGX_RTMP_MSG_BANDWIDTH);
|
||||
{
|
||||
NGX_RTMP_USER_START(s, NGX_RTMP_MSG_BANDWIDTH);
|
||||
|
||||
NGX_RTMP_USER_OUT4(ack_size);
|
||||
NGX_RTMP_USER_OUT1(limit_type);
|
||||
NGX_RTMP_USER_OUT4(ack_size);
|
||||
NGX_RTMP_USER_OUT1(limit_type);
|
||||
|
||||
NGX_RTMP_USER_END(s);
|
||||
NGX_RTMP_USER_END(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -188,11 +200,13 @@ ngx_rtmp_create_stream_begin(ngx_rtmp_session_t *s, uint32_t msid)
|
|||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"create: stream_begin msid=%uD", msid);
|
||||
|
||||
NGX_RTMP_UCTL_START(s, NGX_RTMP_MSG_USER, NGX_RTMP_USER_STREAM_BEGIN);
|
||||
{
|
||||
NGX_RTMP_UCTL_START(s, NGX_RTMP_MSG_USER, NGX_RTMP_USER_STREAM_BEGIN);
|
||||
|
||||
NGX_RTMP_USER_OUT4(msid);
|
||||
NGX_RTMP_USER_OUT4(msid);
|
||||
|
||||
NGX_RTMP_USER_END(s);
|
||||
NGX_RTMP_USER_END(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -209,12 +223,14 @@ 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);
|
||||
|
||||
NGX_RTMP_USER_OUT4(msid);
|
||||
{
|
||||
NGX_RTMP_UCTL_START(s, NGX_RTMP_MSG_USER, NGX_RTMP_USER_STREAM_EOF);
|
||||
|
||||
NGX_RTMP_USER_END(s);
|
||||
NGX_RTMP_USER_OUT4(msid);
|
||||
|
||||
NGX_RTMP_USER_END(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -232,11 +248,13 @@ ngx_rtmp_create_stream_dry(ngx_rtmp_session_t *s, uint32_t msid)
|
|||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"create: stream_dry msid=%uD", msid);
|
||||
|
||||
NGX_RTMP_UCTL_START(s, NGX_RTMP_MSG_USER, NGX_RTMP_USER_STREAM_DRY);
|
||||
{
|
||||
NGX_RTMP_UCTL_START(s, NGX_RTMP_MSG_USER, NGX_RTMP_USER_STREAM_DRY);
|
||||
|
||||
NGX_RTMP_USER_OUT4(msid);
|
||||
NGX_RTMP_USER_OUT4(msid);
|
||||
|
||||
NGX_RTMP_USER_END(s);
|
||||
NGX_RTMP_USER_END(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -249,24 +267,26 @@ 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,
|
||||
"create: set_buflen msid=%uD buflen=%uD",
|
||||
msid, buflen_msec);
|
||||
|
||||
NGX_RTMP_UCTL_START(s, NGX_RTMP_MSG_USER, NGX_RTMP_USER_SET_BUFLEN);
|
||||
{
|
||||
NGX_RTMP_UCTL_START(s, NGX_RTMP_MSG_USER, NGX_RTMP_USER_SET_BUFLEN);
|
||||
|
||||
NGX_RTMP_USER_OUT4(msid);
|
||||
NGX_RTMP_USER_OUT4(buflen_msec);
|
||||
NGX_RTMP_USER_OUT4(msid);
|
||||
NGX_RTMP_USER_OUT4(buflen_msec);
|
||||
|
||||
NGX_RTMP_USER_END(s);
|
||||
NGX_RTMP_USER_END(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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,
|
||||
|
@ -275,21 +295,23 @@ 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);
|
||||
|
||||
NGX_RTMP_UCTL_START(s, NGX_RTMP_MSG_USER, NGX_RTMP_USER_RECORDED);
|
||||
{
|
||||
NGX_RTMP_UCTL_START(s, NGX_RTMP_MSG_USER, NGX_RTMP_USER_RECORDED);
|
||||
|
||||
NGX_RTMP_USER_OUT4(msid);
|
||||
NGX_RTMP_USER_OUT4(msid);
|
||||
|
||||
NGX_RTMP_USER_END(s);
|
||||
NGX_RTMP_USER_END(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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));
|
||||
|
@ -297,21 +319,23 @@ 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);
|
||||
|
||||
NGX_RTMP_UCTL_START(s, NGX_RTMP_MSG_USER, NGX_RTMP_USER_PING_REQUEST);
|
||||
{
|
||||
NGX_RTMP_UCTL_START(s, NGX_RTMP_MSG_USER, NGX_RTMP_USER_PING_REQUEST);
|
||||
|
||||
NGX_RTMP_USER_OUT4(timestamp);
|
||||
NGX_RTMP_USER_OUT4(timestamp);
|
||||
|
||||
NGX_RTMP_USER_END(s);
|
||||
NGX_RTMP_USER_END(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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));
|
||||
|
@ -319,28 +343,30 @@ 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);
|
||||
|
||||
NGX_RTMP_UCTL_START(s, NGX_RTMP_MSG_USER, NGX_RTMP_USER_PING_RESPONSE);
|
||||
{
|
||||
NGX_RTMP_UCTL_START(s, NGX_RTMP_MSG_USER, NGX_RTMP_USER_PING_RESPONSE);
|
||||
|
||||
NGX_RTMP_USER_OUT4(timestamp);
|
||||
NGX_RTMP_USER_OUT4(timestamp);
|
||||
|
||||
NGX_RTMP_USER_END(s);
|
||||
NGX_RTMP_USER_END(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
@ -352,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;
|
||||
|
@ -436,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 },
|
||||
|
||||
|
@ -476,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));
|
||||
|
||||
|
@ -483,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]));
|
||||
}
|
||||
|
||||
|
@ -506,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 },
|
||||
|
||||
|
@ -553,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]));
|
||||
}
|
||||
|
||||
|
@ -567,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)
|
||||
{
|
||||
|
@ -576,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 },
|
||||
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#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;
|
||||
|
@ -22,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);
|
||||
|
@ -53,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;
|
||||
|
@ -63,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;
|
||||
|
@ -73,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;
|
||||
|
@ -102,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) {
|
||||
|
|
|
@ -1,19 +1,26 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <nginx.h>
|
||||
#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);
|
||||
|
||||
|
||||
|
@ -24,6 +31,7 @@ static time_t start_time;
|
|||
#define NGX_RTMP_STAT_GLOBAL 0x01
|
||||
#define NGX_RTMP_STAT_LIVE 0x02
|
||||
#define NGX_RTMP_STAT_CLIENTS 0x04
|
||||
#define NGX_RTMP_STAT_PLAY 0x08
|
||||
|
||||
/*
|
||||
* global: stat-{bufs-{total,free,used}, total bytes in/out, bw in/out} - cscf
|
||||
|
@ -42,14 +50,14 @@ 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[] = {
|
||||
|
||||
{ ngx_string("rtmp_stat"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
|
||||
ngx_conf_set_bitmask_slot,
|
||||
ngx_rtmp_stat,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_rtmp_stat_loc_conf_t, stat),
|
||||
ngx_rtmp_stat_masks },
|
||||
|
@ -66,40 +74,54 @@ 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 */
|
||||
NULL, /* 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
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define NGX_RTMP_STAT_BUFSIZE 256
|
||||
|
||||
|
||||
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_queue_t*) &ngx_rtmp_init_queue);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
/* ngx_escape_html does not escape characters out of ASCII range
|
||||
* which are bad for xslt */
|
||||
|
||||
|
@ -137,6 +159,13 @@ ngx_rtmp_stat_escape(ngx_http_request_t *r, void *data, size_t len)
|
|||
return new_data;
|
||||
}
|
||||
|
||||
#if (NGX_WIN32)
|
||||
/*
|
||||
* Fix broken MSVC memcpy optimization for 4-byte data
|
||||
* when this function is inlined
|
||||
*/
|
||||
__declspec(noinline)
|
||||
#endif
|
||||
|
||||
static void
|
||||
ngx_rtmp_stat_output(ngx_http_request_t *r, ngx_chain_t ***lll,
|
||||
|
@ -171,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;
|
||||
|
@ -217,41 +246,46 @@ 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_OFF_T_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),
|
||||
"%uz", 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),
|
||||
"%uz", 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),
|
||||
"%uz", 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),
|
||||
"%uz", 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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef NGX_RTMP_POOL_DEBUG
|
||||
static void
|
||||
ngx_rtmp_stat_get_pool_size(ngx_pool_t *pool, ngx_uint_t *nlarge,
|
||||
size_t *size)
|
||||
ngx_uint_t *size)
|
||||
{
|
||||
ngx_pool_large_t *l;
|
||||
ngx_pool_t *p, *n;
|
||||
|
@ -272,41 +306,155 @@ 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_t size;
|
||||
u_char buf[NGX_OFF_T_LEN + 1];
|
||||
ngx_uint_t nlarge, size;
|
||||
u_char buf[NGX_INT_T_LEN];
|
||||
|
||||
size = 0;
|
||||
nlarge = 0;
|
||||
ngx_rtmp_stat_get_pool_size(pool, &nlarge, &size);
|
||||
NGX_RTMP_STAT_L("<pool><nlarge>");
|
||||
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
|
||||
"%ui", nlarge) - buf);
|
||||
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%ui", nlarge) - buf);
|
||||
NGX_RTMP_STAT_L("</nlarge><size>");
|
||||
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
|
||||
"%uz", size) - buf);
|
||||
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%ui", size) - buf);
|
||||
NGX_RTMP_STAT_L("</size></pool>\r\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
static void
|
||||
ngx_rtmp_stat_live(ngx_http_request_t *r, ngx_chain_t ***lll,
|
||||
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);
|
||||
#endif
|
||||
NGX_RTMP_STAT_L("<id>");
|
||||
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%ui",
|
||||
(ngx_uint_t) s->connection->number) - buf);
|
||||
NGX_RTMP_STAT_L("</id>");
|
||||
|
||||
NGX_RTMP_STAT_L("<address>");
|
||||
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);
|
||||
NGX_RTMP_STAT_L("</time>");
|
||||
|
||||
if (s->flashver.len) {
|
||||
NGX_RTMP_STAT_L("<flashver>");
|
||||
NGX_RTMP_STAT_ES(&s->flashver);
|
||||
NGX_RTMP_STAT_L("</flashver>");
|
||||
}
|
||||
|
||||
if (s->page_url.len) {
|
||||
NGX_RTMP_STAT_L("<pageurl>");
|
||||
NGX_RTMP_STAT_ES(&s->page_url);
|
||||
NGX_RTMP_STAT_L("</pageurl>");
|
||||
}
|
||||
|
||||
if (s->swf_url.len) {
|
||||
NGX_RTMP_STAT_L("<swfurl>");
|
||||
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_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;
|
||||
size_t nclients, total_nclients;
|
||||
u_char buf[NGX_OFF_T_LEN + 1];
|
||||
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;
|
||||
}
|
||||
|
||||
slcf = ngx_http_get_module_loc_conf(r, ngx_rtmp_stat_module);
|
||||
|
||||
NGX_RTMP_STAT_L("<live>\r\n");
|
||||
|
@ -316,16 +464,28 @@ 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");
|
||||
|
||||
NGX_RTMP_STAT_L("<time>");
|
||||
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
|
||||
"%M", ngx_current_msec - stream->epoch) - buf);
|
||||
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%i",
|
||||
(ngx_int_t) (ngx_current_msec - stream->epoch))
|
||||
- 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;
|
||||
|
@ -334,57 +494,25 @@ ngx_rtmp_stat_live(ngx_http_request_t *r, ngx_chain_t ***lll,
|
|||
if (slcf->stat & NGX_RTMP_STAT_CLIENTS) {
|
||||
NGX_RTMP_STAT_L("<client>");
|
||||
|
||||
#ifdef NGX_RTMP_POOL_DEBUG
|
||||
ngx_rtmp_stat_dump_pool(r, lll, s->connection->pool);
|
||||
#endif
|
||||
NGX_RTMP_STAT_L("<id>");
|
||||
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%ui",
|
||||
(ngx_uint_t) s->connection->number)
|
||||
- buf);
|
||||
NGX_RTMP_STAT_L("</id>");
|
||||
|
||||
NGX_RTMP_STAT_L("<address>");
|
||||
NGX_RTMP_STAT_S(&s->connection->addr_text);
|
||||
NGX_RTMP_STAT_L("</address>");
|
||||
|
||||
NGX_RTMP_STAT_L("<time>");
|
||||
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%M",
|
||||
ngx_current_msec - s->epoch)
|
||||
- buf);
|
||||
NGX_RTMP_STAT_L("</time>");
|
||||
ngx_rtmp_stat_client(r, lll, s);
|
||||
|
||||
NGX_RTMP_STAT_L("<dropped>");
|
||||
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
|
||||
"%uD/%uD", ctx->cs[1].dropped,
|
||||
ctx->cs[0].dropped) - 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(buf, ngx_snprintf(buf, sizeof(buf),
|
||||
"%L", (int64_t) ctx->cs[1].timestamp -
|
||||
(int64_t) ctx->cs[0].timestamp)
|
||||
- buf);
|
||||
NGX_RTMP_STAT(bbuf, ngx_snprintf(bbuf, sizeof(bbuf),
|
||||
"%D", ctx->cs[1].timestamp -
|
||||
ctx->cs[0].timestamp) - bbuf);
|
||||
}
|
||||
NGX_RTMP_STAT_L("</avsync>");
|
||||
|
||||
if (s->flashver.len) {
|
||||
NGX_RTMP_STAT_L("<flashver>");
|
||||
NGX_RTMP_STAT_ES(&s->flashver);
|
||||
NGX_RTMP_STAT_L("</flashver>");
|
||||
}
|
||||
|
||||
if (s->page_url.len) {
|
||||
NGX_RTMP_STAT_L("<pageurl>");
|
||||
NGX_RTMP_STAT_ES(&s->page_url);
|
||||
NGX_RTMP_STAT_L("</pageurl>");
|
||||
}
|
||||
|
||||
if (s->swf_url.len) {
|
||||
NGX_RTMP_STAT_L("<swfurl>");
|
||||
NGX_RTMP_STAT_ES(&s->swf_url);
|
||||
NGX_RTMP_STAT_L("</swfurl>");
|
||||
}
|
||||
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/>");
|
||||
|
@ -394,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) {
|
||||
|
@ -403,44 +543,102 @@ 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),
|
||||
"%ui", codec->width) - 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),
|
||||
"%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(buf, ngx_snprintf(buf, sizeof(buf),
|
||||
"%ui", codec->height) - buf);
|
||||
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),
|
||||
"%uz", nclients) - buf);
|
||||
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
|
||||
"%ui", nclients) - buf);
|
||||
NGX_RTMP_STAT_L("</nclients>\r\n");
|
||||
|
||||
if (stream->publishing) {
|
||||
|
@ -451,13 +649,17 @@ 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),
|
||||
"%uz", total_nclients) - buf);
|
||||
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
|
||||
"%ui", total_nclients) - buf);
|
||||
NGX_RTMP_STAT_L("</nclients>\r\n");
|
||||
|
||||
NGX_RTMP_STAT_L("</live>\r\n");
|
||||
|
@ -465,7 +667,79 @@ ngx_rtmp_stat_live(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_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) {
|
||||
return;
|
||||
}
|
||||
|
||||
slcf = ngx_http_get_module_loc_conf(r, ngx_rtmp_stat_module);
|
||||
|
||||
NGX_RTMP_STAT_L("<play>\r\n");
|
||||
|
||||
total_nclients = 0;
|
||||
for (n = 0; n < pacf->nbuckets; ++n) {
|
||||
for (ctx = pacf->ctx[n]; ctx; ) {
|
||||
NGX_RTMP_STAT_L("<stream>\r\n");
|
||||
|
||||
NGX_RTMP_STAT_L("<name>");
|
||||
NGX_RTMP_STAT_ECS(ctx->name);
|
||||
NGX_RTMP_STAT_L("</name>\r\n");
|
||||
|
||||
nclients = 0;
|
||||
sctx = ctx;
|
||||
for (; ctx; ctx = ctx->next) {
|
||||
if (ngx_strcmp(ctx->name, sctx->name)) {
|
||||
break;
|
||||
}
|
||||
|
||||
nclients++;
|
||||
|
||||
s = ctx->session;
|
||||
if (slcf->stat & NGX_RTMP_STAT_CLIENTS) {
|
||||
NGX_RTMP_STAT_L("<client>");
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
total_nclients += nclients;
|
||||
|
||||
NGX_RTMP_STAT_L("<active/>");
|
||||
NGX_RTMP_STAT_L("<nclients>");
|
||||
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
|
||||
"%ui", nclients) - buf);
|
||||
NGX_RTMP_STAT_L("</nclients>\r\n");
|
||||
|
||||
NGX_RTMP_STAT_L("</stream>\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
NGX_RTMP_STAT_L("<nclients>");
|
||||
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
|
||||
"%ui", total_nclients) - buf);
|
||||
NGX_RTMP_STAT_L("</nclients>\r\n");
|
||||
|
||||
NGX_RTMP_STAT_L("</play>\r\n");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
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;
|
||||
|
@ -478,16 +752,21 @@ 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,
|
||||
cacf->app_conf[ngx_rtmp_play_module.ctx_index]);
|
||||
}
|
||||
|
||||
NGX_RTMP_STAT_L("</application>\r\n");
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
@ -517,10 +796,9 @@ ngx_rtmp_stat_handler(ngx_http_request_t *r)
|
|||
ngx_chain_t *cl, *l, **ll, ***lll;
|
||||
size_t n;
|
||||
off_t len;
|
||||
static u_char tbuf[NGX_TIME_T_LEN + 1];
|
||||
static u_char nbuf[NGX_OFF_T_LEN + 1];
|
||||
static u_char tbuf[NGX_TIME_T_LEN];
|
||||
static u_char nbuf[NGX_INT_T_LEN];
|
||||
|
||||
r->keepalive = 0;
|
||||
slcf = ngx_http_get_module_loc_conf(r, ngx_rtmp_stat_module);
|
||||
if (slcf->stat == 0) {
|
||||
return NGX_DECLINED;
|
||||
|
@ -545,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),
|
||||
|
@ -560,10 +845,16 @@ ngx_rtmp_stat_handler(ngx_http_request_t *r)
|
|||
|
||||
NGX_RTMP_STAT_L("<uptime>");
|
||||
NGX_RTMP_STAT(tbuf, ngx_snprintf(tbuf, sizeof(tbuf),
|
||||
"%T", ngx_cached_time->sec - start_time) - tbuf);
|
||||
"%T", ngx_cached_time->sec - start_time) - tbuf);
|
||||
NGX_RTMP_STAT_L("</uptime>\r\n");
|
||||
|
||||
ngx_rtmp_stat_bw(r, lll, &ngx_rtmp_bw_in, &ngx_rtmp_bw_out);
|
||||
NGX_RTMP_STAT_L("<naccepted>");
|
||||
NGX_RTMP_STAT(nbuf, ngx_snprintf(nbuf, sizeof(nbuf),
|
||||
"%ui", ngx_rtmp_naccepted) - nbuf);
|
||||
NGX_RTMP_STAT_L("</naccepted>\r\n");
|
||||
|
||||
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) {
|
||||
|
@ -619,22 +910,22 @@ ngx_rtmp_stat_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
|||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_rtmp_stat(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
|
||||
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
|
||||
clcf->handler = ngx_rtmp_stat_handler;
|
||||
|
||||
return ngx_conf_set_bitmask_slot(cf, cmd, conf);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_stat_postconfiguration(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_handler_pt *h;
|
||||
ngx_http_core_main_conf_t *cmcf;
|
||||
|
||||
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
|
||||
|
||||
h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
*h = ngx_rtmp_stat_handler;
|
||||
|
||||
start_time = ngx_cached_time->sec;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
|
|
15
ngx_rtmp_version.h
Normal file
15
ngx_rtmp_version.h
Normal 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_ */
|
231
stat.xsl
231
stat.xsl
|
@ -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> <xsl:value-of select="/rtmp/version"/>,
|
||||
Generated by <a href='https://github.com/arut/nginx-rtmp-module'>
|
||||
nginx-rtmp-module</a> <xsl:value-of select="/rtmp/nginx_rtmp_version"/>,
|
||||
<a href="http://nginx.org">nginx</a> <xsl:value-of select="/rtmp/nginx_version"/>,
|
||||
pid <xsl:value-of select="/rtmp/pid"/>,
|
||||
built <xsl:value-of select="/rtmp/built"/> <xsl:value-of select="/rtmp/compiler"/>
|
||||
</body>
|
||||
|
@ -24,24 +31,52 @@
|
|||
<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>In Kbps</th>
|
||||
<th>Out Kbps</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"/>
|
||||
<td><xsl:value-of select="in"/></td>
|
||||
<td><xsl:value-of select="out"/></td>
|
||||
<td><xsl:value-of select="round(bwin div 1024)"/></td>
|
||||
<td><xsl:value-of select="round(bwout div 1024)"/></td>
|
||||
<td colspan="5"/>
|
||||
<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="bytes_in"/>
|
||||
</xsl:call-template>
|
||||
</td>
|
||||
<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="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"/>
|
||||
|
@ -63,6 +98,7 @@
|
|||
</td>
|
||||
</tr>
|
||||
<xsl:apply-templates select="live"/>
|
||||
<xsl:apply-templates select="play"/>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="live">
|
||||
|
@ -77,6 +113,18 @@
|
|||
<xsl:apply-templates select="stream"/>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="play">
|
||||
<tr bgcolor="#aaaaaa">
|
||||
<td>
|
||||
<i>vod streams</i>
|
||||
</td>
|
||||
<td align="middle">
|
||||
<xsl:value-of select="nclients"/>
|
||||
</td>
|
||||
</tr>
|
||||
<xsl:apply-templates select="stream"/>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="stream">
|
||||
<tr valign="top">
|
||||
<xsl:attribute name="bgcolor">
|
||||
|
@ -99,19 +147,64 @@
|
|||
</a>
|
||||
</td>
|
||||
<td align="middle"> <xsl:value-of select="nclients"/> </td>
|
||||
<td><xsl:value-of select="in"/></td>
|
||||
<td><xsl:value-of select="out"/></td>
|
||||
<td><xsl:value-of select="round(bwin div 1024)"/></td>
|
||||
<td><xsl:value-of select="round(bwout div 1024)"/></td>
|
||||
<td><xsl:value-of select="meta/width"/>x<xsl:value-of select="meta/height"/></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"/>
|
||||
<xsl:value-of select="meta/video/codec"/> <xsl:value-of select="meta/video/profile"/> <xsl:value-of select="meta/video/level"/>
|
||||
</td>
|
||||
<td>
|
||||
<xsl:call-template name="showsize">
|
||||
<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"/> <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="bytes_out"/>
|
||||
</xsl:call-template>
|
||||
</td>
|
||||
<td>
|
||||
<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><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"/>
|
||||
|
@ -122,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>
|
||||
|
@ -144,25 +239,54 @@
|
|||
<xsl:template name="showtime">
|
||||
<xsl:param name="time"/>
|
||||
|
||||
<xsl:variable name="sec">
|
||||
<xsl:value-of select="floor($time div 1000)"/>
|
||||
</xsl:variable>
|
||||
<xsl:if test="$time > 0">
|
||||
<xsl:variable name="sec">
|
||||
<xsl:value-of select="floor($time div 1000)"/>
|
||||
</xsl:variable>
|
||||
|
||||
<xsl:if test="$sec >= 86400">
|
||||
<xsl:value-of select="floor($sec div 86400)"/>d
|
||||
<xsl:if test="$sec >= 86400">
|
||||
<xsl:value-of select="floor($sec div 86400)"/>d
|
||||
</xsl:if>
|
||||
|
||||
<xsl:if test="$sec >= 3600">
|
||||
<xsl:value-of select="(floor($sec div 3600)) mod 24"/>h
|
||||
</xsl:if>
|
||||
|
||||
<xsl:if test="$sec >= 60">
|
||||
<xsl:value-of select="(floor($sec div 60)) mod 60"/>m
|
||||
</xsl:if>
|
||||
|
||||
<xsl:value-of select="$sec mod 60"/>s
|
||||
</xsl:if>
|
||||
|
||||
<xsl:if test="$sec >= 3600">
|
||||
<xsl:value-of select="(floor($sec div 3600)) mod 24"/>h
|
||||
</xsl:if>
|
||||
|
||||
<xsl:if test="$sec >= 60">
|
||||
<xsl:value-of select="(floor($sec div 60)) mod 60"/>m
|
||||
</xsl:if>
|
||||
|
||||
<xsl:value-of select="$sec mod 60"/>s
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="showsize">
|
||||
<xsl:param name="size"/>
|
||||
<xsl:param name="bits" select="0" />
|
||||
<xsl:param name="persec" select="0" />
|
||||
<xsl:variable name="sizen">
|
||||
<xsl:value-of select="floor($size div 1024)"/>
|
||||
</xsl:variable>
|
||||
<xsl:choose>
|
||||
<xsl:when test="$sizen >= 1073741824">
|
||||
<xsl:value-of select="format-number($sizen div 1073741824,'#.###')"/> T</xsl:when>
|
||||
|
||||
<xsl:when test="$sizen >= 1048576">
|
||||
<xsl:value-of select="format-number($sizen div 1048576,'#.###')"/> G</xsl:when>
|
||||
|
||||
<xsl:when test="$sizen >= 1024">
|
||||
<xsl:value-of select="format-number($sizen div 1024,'#.##')"/> M</xsl:when>
|
||||
<xsl:when test="$sizen >= 0">
|
||||
<xsl:value-of select="$sizen"/> K</xsl:when>
|
||||
</xsl:choose>
|
||||
<xsl:if test="string-length($size) > 0">
|
||||
<xsl:choose>
|
||||
<xsl:when test="$bits = 1">b</xsl:when>
|
||||
<xsl:otherwise>B</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
<xsl:if test="$persec = 1">/s</xsl:if>
|
||||
</xsl:if>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="streamstate">
|
||||
<xsl:choose>
|
||||
|
@ -179,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>
|
||||
|
@ -190,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?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">
|
||||
|
@ -202,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">
|
||||
|
@ -219,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>
|
||||
|
|
|
@ -1,70 +1,69 @@
|
|||
<?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()">
|
||||
xmlns:s="library://ns.adobe.com/flex/spark"
|
||||
xmlns:mx="library://ns.adobe.com/flex/mx"
|
||||
minWidth="500" minHeight="350" creationComplete="init()">
|
||||
|
||||
<fx:Declarations>
|
||||
<!-- Place non-visual elements (e.g., services, value objects) here -->
|
||||
</fx:Declarations>
|
||||
<fx:Script>
|
||||
<![CDATA[
|
||||
import mx.controls.Alert;
|
||||
import mx.core.FlexGlobals;
|
||||
import flash.display.StageDisplayState;
|
||||
import mx.managers.SystemManager;
|
||||
|
||||
<fx:Script>
|
||||
<![CDATA[
|
||||
import mx.controls.Alert;
|
||||
import mx.core.FlexGlobals;
|
||||
private var streamer:String;
|
||||
private var file:String;
|
||||
private var streamer:String;
|
||||
private var file:String;
|
||||
|
||||
private function toggleFeedListener(event:MouseEvent):void {
|
||||
if(toggleFeed.label == 'Start Feed') {
|
||||
toggleFeed.label = 'Stop Feed';
|
||||
toggleAudio.enabled = true;
|
||||
videoDisplay.play();
|
||||
} else {
|
||||
toggleFeed.label = 'Start Feed';
|
||||
toggleAudio.enabled = false;
|
||||
videoDisplay.close();
|
||||
}
|
||||
}
|
||||
private function playButtonListener(event:MouseEvent):void {
|
||||
if(playButton.label == 'Play') {
|
||||
playButton.label = 'Stop';
|
||||
videoDisplay.source = streamer + "/" + file;
|
||||
videoDisplay.play();
|
||||
} else {
|
||||
playButton.label = 'Play';
|
||||
videoDisplay.source = "";
|
||||
//videoDisplay.close();
|
||||
}
|
||||
}
|
||||
|
||||
private function toggleAudioHandler(event:MouseEvent):void {
|
||||
if(toggleAudio.label == 'Unmute') {
|
||||
toggleAudio.label = 'Mute';
|
||||
videoDisplay.volume = 1;
|
||||
} else {
|
||||
toggleAudio.label = 'Unmute';
|
||||
videoDisplay.volume = 0;
|
||||
}
|
||||
}
|
||||
private function fullscreenButtonListener(event:MouseEvent):void {
|
||||
try {
|
||||
switch (systemManager.stage.displayState) {
|
||||
case StageDisplayState.FULL_SCREEN:
|
||||
stage.displayState = StageDisplayState.NORMAL;
|
||||
break;
|
||||
default:
|
||||
stage.displayState = StageDisplayState.FULL_SCREEN;
|
||||
break;
|
||||
}
|
||||
} catch (err:SecurityError) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
private function initListeners():void {
|
||||
toggleFeed.addEventListener(MouseEvent.CLICK, toggleFeedListener);
|
||||
toggleAudio.addEventListener(MouseEvent.CLICK, toggleAudioHandler);
|
||||
}
|
||||
private function init():void {
|
||||
videoDisplay.mx_internal::videoPlayer.bufferTime = 1;
|
||||
|
||||
private function init():void {
|
||||
streamer = FlexGlobals.topLevelApplication.parameters.streamer;
|
||||
file = FlexGlobals.topLevelApplication.parameters.file;
|
||||
if(file == null) {
|
||||
Alert.show('Missing flashvars: file');
|
||||
return;
|
||||
}
|
||||
if(streamer == null) {
|
||||
Alert.show('Missing flashvars: streamer');
|
||||
return;
|
||||
}
|
||||
videoDisplay.source = streamer + "/" + file;
|
||||
initListeners();
|
||||
}
|
||||
]]>
|
||||
</fx:Script>
|
||||
<s:Panel x="0" y="0" width="100%" height="100%" title="RTMP Player">
|
||||
<mx:VideoDisplay width="100%" height="100%" id="videoDisplay" autoPlay="false">
|
||||
</mx:VideoDisplay>
|
||||
<s:controlBarContent>
|
||||
<s:Button label="Start Feed" id="toggleFeed"></s:Button>
|
||||
<s:Spacer width="100%" height="100%"/>
|
||||
<s:Button label="Mute" id="toggleAudio" enabled="false"></s:Button>
|
||||
</s:controlBarContent>
|
||||
</s:Panel>
|
||||
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;
|
||||
}
|
||||
|
||||
playButton.addEventListener(MouseEvent.CLICK, playButtonListener);
|
||||
fullscreenButton.addEventListener(MouseEvent.CLICK, fullscreenButtonListener);
|
||||
}
|
||||
]]>
|
||||
</fx:Script>
|
||||
<s:BorderContainer x="0" y="0" width="100%" height="100%">
|
||||
<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>
|
||||
</s:Application>
|
||||
|
|
Binary file not shown.
101
test/rtmp-publisher/RtmpPlayerLight.mxml
Normal file
101
test/rtmp-publisher/RtmpPlayerLight.mxml
Normal 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>
|
BIN
test/rtmp-publisher/RtmpPlayerLight.swf
Normal file
BIN
test/rtmp-publisher/RtmpPlayerLight.swf
Normal file
Binary file not shown.
|
@ -4,14 +4,13 @@
|
|||
xmlns:mx="library://ns.adobe.com/flex/mx"
|
||||
minWidth="500" minHeight="350" creationComplete="init()">
|
||||
|
||||
<fx:Declarations>
|
||||
<!-- Place non-visual elements (e.g., services, value objects) here -->
|
||||
</fx:Declarations>
|
||||
|
||||
<fx:Script>
|
||||
<![CDATA[
|
||||
import mx.controls.Alert;
|
||||
import mx.core.FlexGlobals;
|
||||
import mx.events.FlexEvent;
|
||||
import spark.skins.spark.PanelSkin;
|
||||
|
||||
private var streamer:String;
|
||||
private var file:String;
|
||||
private var camera:Camera;
|
||||
|
@ -20,50 +19,21 @@
|
|||
private var stream:NetStream;
|
||||
private var h264Settings:H264VideoStreamSettings;
|
||||
|
||||
private function toggleFeedListener(event:MouseEvent):void {
|
||||
if(toggleFeed.label == 'Start Feed') {
|
||||
toggleFeed.label = 'Stop Feed';
|
||||
stream.publish(file, 'live');
|
||||
videoDisplay.attachCamera(camera);
|
||||
toggleVideo.enabled = true;
|
||||
toggleAudio.enabled = true;
|
||||
private function publishButtonListener(event:MouseEvent):void {
|
||||
if(publishButton.label == 'Publish') {
|
||||
publishButton.label = 'Stop';
|
||||
connection = new NetConnection();
|
||||
connection.connect(streamer);
|
||||
connection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHander);
|
||||
} else {
|
||||
toggleFeed.label = 'Start Feed';
|
||||
publishButton.label = 'Publish';
|
||||
stream.close();
|
||||
videoDisplay.attachCamera(null);
|
||||
toggleVideo.enabled = false;
|
||||
toggleAudio.enabled = false;
|
||||
connection.close();
|
||||
stream = null;
|
||||
connection = null;
|
||||
}
|
||||
}
|
||||
|
||||
private function toggleVideoListener(event:MouseEvent):void {
|
||||
if(toggleVideo.label == 'Start Video') {
|
||||
toggleVideo.label = 'Stop Video';
|
||||
videoDisplay.attachCamera(camera);
|
||||
stream.attachCamera(camera);
|
||||
} else {
|
||||
toggleVideo.label = 'Start Video';
|
||||
videoDisplay.attachCamera(null);
|
||||
stream.attachCamera(null);
|
||||
}
|
||||
}
|
||||
|
||||
private function toggleAudioListener(event:MouseEvent):void {
|
||||
if(toggleAudio.label == 'Start Audio') {
|
||||
toggleAudio.label = 'Stop Audio';
|
||||
stream.attachAudio(microphone);
|
||||
} else {
|
||||
toggleAudio.label = 'Start Audio';
|
||||
stream.attachAudio(null);
|
||||
}
|
||||
}
|
||||
|
||||
private function initListeners():void {
|
||||
toggleFeed.addEventListener(MouseEvent.CLICK, toggleFeedListener);
|
||||
toggleVideo.addEventListener(MouseEvent.CLICK, toggleVideoListener);
|
||||
toggleAudio.addEventListener(MouseEvent.CLICK, toggleAudioListener);
|
||||
}
|
||||
|
||||
private function netStatusHander(event:NetStatusEvent):void {
|
||||
switch(event.info.code) {
|
||||
case 'NetConnection.Connect.Success':
|
||||
|
@ -71,47 +41,46 @@
|
|||
stream.attachCamera(camera);
|
||||
stream.attachAudio(microphone);
|
||||
h264Settings = new H264VideoStreamSettings();
|
||||
h264Settings.setProfileLevel(H264Profile.BASELINE, H264Level.LEVEL_1_2);
|
||||
h264Settings.setProfileLevel(H264Profile.BASELINE, H264Level.LEVEL_3_1);
|
||||
stream.videoStreamSettings = h264Settings;
|
||||
stream.publish(file, 'live');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function init():void {
|
||||
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;
|
||||
}
|
||||
if(streamer == null) {
|
||||
Alert.show('Missing flashvars: streamer');
|
||||
return;
|
||||
}
|
||||
initListeners();
|
||||
|
||||
publishButton.addEventListener(MouseEvent.CLICK, publishButtonListener);
|
||||
|
||||
camera = Camera.getCamera();
|
||||
camera.setMode(640, 480, 30);
|
||||
camera.setQuality(131072, 70);
|
||||
|
||||
//videoDisplay.attachCamera(camera);
|
||||
var localCam:Video = new Video(640,480);
|
||||
localCam.attachCamera(camera);
|
||||
videoDisplay.addChild(localCam);
|
||||
|
||||
microphone = Microphone.getMicrophone();
|
||||
microphone.setSilenceLevel(0);
|
||||
microphone.codec = "Speex";
|
||||
microphone.encodeQuality = 6;
|
||||
camera.setMode(704, 576, 25);
|
||||
camera.setQuality(131072, 70);
|
||||
connection = new NetConnection();
|
||||
connection.connect(streamer);
|
||||
connection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHander);
|
||||
|
||||
}
|
||||
]]>
|
||||
</fx:Script>
|
||||
<s:Panel x="0" y="0" width="100%" height="100%" title="RTMP Publisher">
|
||||
<mx:VideoDisplay width="100%" height="100%" id="videoDisplay">
|
||||
</mx:VideoDisplay>
|
||||
<s:controlBarContent>
|
||||
<s:Button label="Start Feed" id="toggleFeed"></s:Button>
|
||||
<s:Spacer width="100%" height="100%"/>
|
||||
<s:Button label="Stop Video" id="toggleVideo" enabled="false"></s:Button>
|
||||
<s:Button label="Stop Audio" id="toggleAudio" enabled="false"></s:Button>
|
||||
</s:controlBarContent>
|
||||
</s:Panel>
|
||||
<s:BorderContainer x="0" y="0" width="100%" height="100%">
|
||||
<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>
|
||||
|
|
Binary file not shown.
|
@ -8,7 +8,10 @@
|
|||
streamer: 'rtmp://localhost/myapp',
|
||||
file:'mystream'
|
||||
};
|
||||
swfobject.embedSWF("RtmpPlayer.swf", "rtmp-publisher", "500", "400", "9.0.0", null, flashVars);
|
||||
var params = {};
|
||||
params.allowfullscreen = "true";
|
||||
var attributes = {};
|
||||
swfobject.embedSWF("RtmpPlayer.swf", "rtmp-publisher", "640", "480", "9.0.0", null, flashVars, params, attributes);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
streamer: 'rtmp://localhost/myapp',
|
||||
file:'mystream'
|
||||
};
|
||||
swfobject.embedSWF("RtmpPublisher.swf", "rtmp-publisher", "500", "400", "9.0.0", null, flashVars);
|
||||
swfobject.embedSWF("RtmpPublisher.swf", "rtmp-publisher", "640", "480", "9.0.0", null, flashVars);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
|
BIN
test/www/bg.jpg
Normal file
BIN
test/www/bg.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
|
@ -5,16 +5,15 @@
|
|||
<div id="container">Loading the player ...</div>
|
||||
<script type="text/javascript">
|
||||
jwplayer("container").setup({
|
||||
modes: [
|
||||
{ type: "flash",
|
||||
src: "/jwplayer/player.swf",
|
||||
config: {
|
||||
bufferlength: 1,
|
||||
file: "mystream",
|
||||
streamer: "rtmp://localhost/myapp",
|
||||
provider: "rtmp",
|
||||
}
|
||||
sources: [
|
||||
{
|
||||
file: "rtmp://localhost/myapp?carg=1/mystream?sarg=2"
|
||||
}
|
||||
]
|
||||
],
|
||||
image: "bg.jpg",
|
||||
autostart: false,
|
||||
width: 640,
|
||||
height: 480,
|
||||
primary: "flash"
|
||||
});
|
||||
</script>
|
||||
|
|
BIN
test/www/jwplayer/jwplayer.flash.swf
Normal file
BIN
test/www/jwplayer/jwplayer.flash.swf
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Loading…
Reference in a new issue