You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

hls-server.cpp 8.0KB

10 maanden geleden
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. #include "aio-worker.h"
  2. #include "aio-socket.h"
  3. #include "mpeg-ps.h"
  4. #include "mpeg-ts.h"
  5. #include "hls-m3u8.h"
  6. #include "hls-media.h"
  7. #include "hls-param.h"
  8. #include "flv-proto.h"
  9. #include "flv-reader.h"
  10. #include "flv-demuxer.h"
  11. #include "http-server.h"
  12. #include "http-route.h"
  13. #include "sys/thread.h"
  14. #include "sys/system.h"
  15. #include "sys/path.h"
  16. #include "cstringext.h"
  17. #include "utf8codec.h"
  18. #include <string.h>
  19. #include <assert.h>
  20. #include <map>
  21. #include <list>
  22. #include <atomic>
  23. #include <vector>
  24. #include <string>
  25. #define CWD "d:\\video\\"
  26. extern "C" int http_list_dir(http_session_t* session, const char* path);
  27. struct hls_ts_t
  28. {
  29. std::atomic<int> ref;
  30. void* data;
  31. size_t size;
  32. std::string name;
  33. };
  34. struct hls_playlist_t
  35. {
  36. pthread_t t;
  37. std::string file;
  38. hls_media_t* hls;
  39. hls_m3u8_t* m3u8;
  40. int64_t pts;
  41. int64_t last_pts;
  42. uint8_t packet[2 * 1024 * 1024];
  43. int i;
  44. std::list<hls_ts_t*> files;
  45. };
  46. static std::map<std::string, hls_playlist_t*> s_playlists;
  47. static int hls_handler(void* param, const void* data, size_t bytes, int64_t pts, int64_t /*dts*/, int64_t duration)
  48. {
  49. hls_playlist_t* playlist = (hls_playlist_t*)param;
  50. int discontinue = 0;
  51. if (playlist->i > 0)
  52. {
  53. discontinue = (playlist->last_pts + HLS_DURATION * 1000 < pts/*discontinue*/ || pts + duration + HLS_DURATION * 1000 < playlist->pts/*rewind*/) ? 1 : 0;
  54. }
  55. playlist->pts = pts;
  56. playlist->last_pts = pts + duration;
  57. char name[128] = { 0 };
  58. snprintf(name, sizeof(name) - 1, "%s/%d.ts", playlist->file.c_str(), playlist->i++);
  59. hls_m3u8_add(playlist->m3u8, name, pts, duration, discontinue);
  60. // add new segment
  61. hls_ts_t* ts = new hls_ts_t;
  62. ts->ref = 1;
  63. ts->name = name;
  64. ts->size = bytes;
  65. ts->data = malloc(bytes);
  66. memcpy(ts->data, data, bytes);
  67. playlist->files.push_back(ts);
  68. // remove oldest segment
  69. while(playlist->files.size() > HLS_LIVE_NUM + 1)
  70. {
  71. ts = playlist->files.front();
  72. playlist->files.pop_front();
  73. if (0 == std::atomic_fetch_sub(&ts->ref, 1) - 1)
  74. {
  75. free(ts->data);
  76. delete ts;
  77. }
  78. }
  79. printf("new segment: %s\n", name);
  80. return 0;
  81. }
  82. static int flv_handler(void* param, int codec, const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags)
  83. {
  84. hls_media_t* hls = (hls_media_t*)param;
  85. switch (codec)
  86. {
  87. case FLV_AUDIO_AAC:
  88. return hls_media_input(hls, PSI_STREAM_AAC, data, bytes, pts, dts, 0);
  89. case FLV_AUDIO_MP3:
  90. return hls_media_input(hls, PSI_STREAM_MP3, data, bytes, pts, dts, 0);
  91. case FLV_VIDEO_H264:
  92. return hls_media_input(hls, PSI_STREAM_H264, data, bytes, pts, dts, flags ? HLS_FLAGS_KEYFRAME : 0);
  93. case FLV_VIDEO_H265:
  94. return hls_media_input(hls, PSI_STREAM_H265, data, bytes, pts, dts, flags ? HLS_FLAGS_KEYFRAME : 0);
  95. default:
  96. // nothing to do
  97. return 0;
  98. }
  99. }
  100. static int STDCALL hls_server_worker(void* param)
  101. {
  102. int r, type;
  103. size_t taglen;
  104. uint64_t clock;
  105. uint32_t timestamp;
  106. hls_playlist_t* playlist = (hls_playlist_t*)param;
  107. std::string file = playlist->file + ".flv";
  108. UTF8Decode utf8(file.c_str());
  109. std::string fullpath = CWD;
  110. fullpath += utf8;
  111. while (1)
  112. {
  113. void* flv = flv_reader_create(fullpath.c_str());
  114. flv_demuxer_t* demuxer = flv_demuxer_create(flv_handler, playlist->hls);
  115. clock = 0;
  116. while (1 == flv_reader_read(flv, &type, &timestamp, &taglen, playlist->packet, sizeof(playlist->packet)))
  117. {
  118. uint64_t now = system_clock();
  119. if (0 == clock)
  120. {
  121. clock = now;
  122. }
  123. else
  124. {
  125. if (timestamp > now - clock)
  126. system_sleep(timestamp - (now - clock));
  127. }
  128. r = flv_demuxer_input(demuxer, type, playlist->packet, taglen, timestamp);
  129. assert(0 == r);
  130. }
  131. flv_demuxer_destroy(demuxer);
  132. flv_reader_destroy(flv);
  133. }
  134. hls_media_destroy(playlist->hls);
  135. //hls_m3u8_destroy(playlist->m3u8);
  136. //s_playlists.erase();
  137. //delete playlist;
  138. return thread_destroy(playlist->t);
  139. }
  140. static int hls_server_m3u8(http_session_t* session, const std::string& path)
  141. {
  142. char playlist[8 * 1024];
  143. hls_m3u8_t* m3u8 = s_playlists.find(path)->second->m3u8;
  144. assert(m3u8);
  145. assert(0 == hls_m3u8_playlist(m3u8, 0, playlist, sizeof(playlist)));
  146. http_server_set_header(session, "content-type", HLS_M3U8_TYPE);
  147. http_server_reply(session, 200, playlist, strlen(playlist));
  148. printf("load %s.m3u8 file\n", path.c_str());
  149. return 0;
  150. }
  151. static int hls_server_ts_onsend(void* param, int code, size_t bytes)
  152. {
  153. hls_ts_t* ts = (hls_ts_t*)param;
  154. if (0 == std::atomic_fetch_sub(&ts->ref, 1) - 1)
  155. {
  156. free(ts->data);
  157. delete ts;
  158. }
  159. return 0;
  160. }
  161. static int hls_server_ts(http_session_t* session, const std::string& path, const std::string& ts)
  162. {
  163. hls_playlist_t* playlist = s_playlists.find(path)->second;
  164. assert(playlist);
  165. std::list<hls_ts_t*>::iterator i;
  166. std::string file = path + '/' + ts;
  167. for(i = playlist->files.begin(); i != playlist->files.end(); ++i)
  168. {
  169. hls_ts_t* ts = *i;
  170. if(ts->name == file)
  171. {
  172. std::atomic_fetch_add(&ts->ref, 1);
  173. http_server_send(session, ts->data, ts->size, hls_server_ts_onsend, ts);
  174. printf("load file %s\n", file.c_str());
  175. return 0;
  176. }
  177. }
  178. printf("load ts file(%s) failed\n", file.c_str());
  179. http_server_set_status_code(session, 404, NULL);
  180. return http_server_send(session, "", 0, NULL, NULL);
  181. }
  182. static int hls_server_onlive(void* /*http*/, http_session_t* session, const char* /*method*/, const char* path)
  183. {
  184. // HTTP CORS
  185. http_server_set_header(session, "Access-Control-Allow-Origin", "*");
  186. http_server_set_header(session, "Access-Control-Allow-Headers", "*");
  187. http_server_set_header(session, "Access-Control-Allow-Credentials", "true");
  188. http_server_set_header(session, "Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS, CONNECT");
  189. path = path + 6;
  190. if (strendswith(path, ".m3u8"))
  191. {
  192. std::string app(path, strlen(path) - 5);
  193. if (s_playlists.find(app) == s_playlists.end())
  194. {
  195. hls_playlist_t* playlist = new hls_playlist_t();
  196. playlist->file = app;
  197. playlist->m3u8 = hls_m3u8_create(HLS_LIVE_NUM, 3);
  198. playlist->hls = hls_media_create(HLS_DURATION * 1000, hls_handler, playlist);
  199. playlist->i = 0;
  200. s_playlists[app] = playlist;
  201. thread_create(&playlist->t, hls_server_worker, playlist);
  202. }
  203. return hls_server_m3u8(session, app);
  204. }
  205. else if (strendswith(path, ".ts"))
  206. {
  207. const char* ts = strchr(path, '/');
  208. std::string app(path, ts ? ts - path : strlen(path));
  209. if (ts && s_playlists.find(app) != s_playlists.end())
  210. {
  211. return hls_server_ts(session, app, ts + 1);
  212. }
  213. }
  214. http_server_set_status_code(session, 404, NULL);
  215. return http_server_send(session, "", 0, NULL, NULL);
  216. }
  217. static int hls_server_onvod(void* /*http*/, http_session_t* session, const char* /*method*/, const char* path)
  218. {
  219. UTF8Decode utf8(path + 5 /* /vod/ */);
  220. std::string fullpath = CWD;
  221. fullpath += utf8;
  222. printf("hls_server_onvod: %s\n", fullpath.c_str());
  223. if (path_testdir(fullpath.c_str()))
  224. {
  225. return http_list_dir(session, fullpath.c_str());
  226. }
  227. else if (path_testfile(fullpath.c_str()))
  228. {
  229. //http_server_set_header(session, "Transfer-Encoding", "chunked");
  230. if (std::string::npos != fullpath.find(".m3u8"))
  231. http_server_set_header(session, "content-type", HLS_M3U8_TYPE);
  232. else if (std::string::npos != fullpath.find(".mpd"))
  233. http_server_set_header(session, "content-type", "application/dash+xml");
  234. else if (std::string::npos != fullpath.find(".mp4") || std::string::npos != fullpath.find(".m4v"))
  235. http_server_set_header(session, "content-type", "video/mp4");
  236. else if (std::string::npos != fullpath.find(".m4a"))
  237. http_server_set_header(session, "content-type", "audio/mp4");
  238. return http_server_sendfile(session, fullpath.c_str(), NULL, NULL);
  239. }
  240. http_server_set_status_code(session, 404, NULL);
  241. return http_server_send(session, "", 0, NULL, NULL);
  242. }
  243. void hls_server_test(const char* ip, int port)
  244. {
  245. aio_worker_init(4);
  246. http_server_t* http = http_server_create(ip, port);
  247. http_server_set_handler(http, http_server_route, http);
  248. http_server_addroute("/live/", hls_server_onlive);
  249. http_server_addroute("/vod/", hls_server_onvod);
  250. // http process
  251. while('q' != getchar())
  252. {
  253. }
  254. http_server_destroy(http);
  255. aio_worker_clean(4);
  256. }
  257. #if defined(_HLS_SERVER_TEST_)
  258. int main(int argc, char* argv[])
  259. {
  260. hls_server_test(NULL, 80);
  261. return 0;
  262. }
  263. #endif