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.

преди 8 месеца
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. #include "aio-worker.h"
  2. #include "dash-mpd.h"
  3. #include "dash-proto.h"
  4. #include "flv-proto.h"
  5. #include "flv-reader.h"
  6. #include "flv-parser.h"
  7. #include "mov-format.h"
  8. #include "http-server.h"
  9. #include "http-route.h"
  10. #include "mpeg4-aac.h"
  11. #include "mpeg4-avc.h"
  12. #include "mpeg4-hevc.h"
  13. #include "cstringext.h"
  14. #include "sys/sync.hpp"
  15. #include "sys/thread.h"
  16. #include "sys/system.h"
  17. #include "sys/path.h"
  18. #include "cpm/shared_ptr.h"
  19. #include "app-log.h"
  20. #include <iostream>
  21. #include <string>
  22. #include <vector>
  23. #include <list>
  24. #include <map>
  25. #define LOCALPATH "./"
  26. struct dash_playlist_t
  27. {
  28. std::string name;
  29. dash_mpd_t* mpd;
  30. int width;
  31. int height;
  32. int64_t timestamp;
  33. int adapation_video;
  34. int adapation_audio;
  35. char playlist[256 * 1024];
  36. uint8_t packet[2 * 1024 * 1024];
  37. std::list<std::string> files;
  38. };
  39. static ThreadLocker s_locker;
  40. extern "C" const struct mov_buffer_t* mov_file_buffer(void);
  41. static int dash_mpd_onsegment(void* param, int /*track*/, const void* data, size_t bytes, int64_t /*pts*/, int64_t /*dts*/, int64_t /*duration*/, const char* name)
  42. {
  43. app_log(LOG_DEBUG, "dash_mpd_onsegment %s\n", name);
  44. FILE* fp = fopen(name, "wb");
  45. if(fp)
  46. {
  47. fwrite(data, 1, bytes, fp);
  48. fclose(fp);
  49. }
  50. dash_playlist_t* dash = (dash_playlist_t*)param;
  51. if(!strendswith(name, "-init.m4v") && !strendswith(name, "-init.m4a"))
  52. dash->files.push_back(name);
  53. while (dash->files.size() > 20)
  54. {
  55. app_log(LOG_DEBUG, "Delete %s\n", dash->files.front().c_str());
  56. path_rmfile(dash->files.front().c_str());
  57. dash->files.pop_front();
  58. }
  59. AutoThreadLocker locker(s_locker);
  60. dash_mpd_playlist(dash->mpd, dash->playlist, sizeof(dash->playlist));
  61. return 0;
  62. }
  63. static int dash_live_onflv(void* param, int codec, const void* data, size_t bytes, uint32_t pts, uint32_t dts, int flags)
  64. {
  65. struct mpeg4_aac_t aac;
  66. struct mpeg4_avc_t avc;
  67. struct mpeg4_hevc_t hevc;
  68. dash_playlist_t* dash = (dash_playlist_t*)param;
  69. switch (codec)
  70. {
  71. case FLV_VIDEO_AVCC:
  72. if (-1 == dash->adapation_video && mpeg4_avc_decoder_configuration_record_load((const uint8_t*)data, bytes, &avc) > 0)
  73. dash->adapation_video = dash_mpd_add_video_adaptation_set(dash->mpd, dash->name.c_str(), MOV_OBJECT_H264, dash->width, dash->height, data, bytes);
  74. break;
  75. case FLV_VIDEO_HVCC:
  76. if (-1 == dash->adapation_video && mpeg4_hevc_decoder_configuration_record_load((const uint8_t*)data, bytes, &hevc) > 0)
  77. dash->adapation_video = dash_mpd_add_video_adaptation_set(dash->mpd, dash->name.c_str(), MOV_OBJECT_HEVC, dash->width, dash->height, data, bytes);
  78. break;
  79. case FLV_AUDIO_ASC:
  80. if (-1 == dash->adapation_audio && mpeg4_aac_audio_specific_config_load((const uint8_t*)data, bytes, &aac) > 0)
  81. {
  82. int rate = mpeg4_aac_audio_frequency_to((enum mpeg4_aac_frequency)aac.sampling_frequency_index);
  83. dash->adapation_audio = dash_mpd_add_audio_adaptation_set(dash->mpd, dash->name.c_str(), MOV_OBJECT_AAC, aac.channel_configuration, 32, rate, data, bytes);
  84. }
  85. break;
  86. case FLV_AUDIO_AAC:
  87. return dash_mpd_input(dash->mpd, dash->adapation_audio, data, bytes, pts, dts, 0);
  88. case FLV_VIDEO_H264:
  89. return dash_mpd_input(dash->mpd, dash->adapation_video, data, bytes, pts, dts, flags ? MOV_AV_FLAG_KEYFREAME : 0);
  90. case FLV_VIDEO_H265:
  91. return dash_mpd_input(dash->mpd, dash->adapation_video, data, bytes, pts, dts, flags ? MOV_AV_FLAG_KEYFREAME : 0);
  92. default:
  93. assert(0);
  94. }
  95. return 0;
  96. }
  97. static int dash_live_worker(const char* file, dash_playlist_t* dash)
  98. {
  99. int r, type;
  100. int avcrecord = 0;
  101. int aacconfig = 0;
  102. size_t taglen;
  103. uint32_t timestamp;
  104. uint32_t s_timestamp = 0;
  105. uint32_t diff = 0;
  106. uint64_t clock;
  107. while (1)
  108. {
  109. void* f = flv_reader_create(file);
  110. clock = system_clock(); // timestamp start from 0
  111. while (1 == flv_reader_read(f, &type, &timestamp, &taglen, dash->packet, sizeof(dash->packet)))
  112. {
  113. uint64_t t = system_clock();
  114. if (clock + timestamp > t && clock + timestamp < t + 3 * 1000)
  115. system_sleep(clock + timestamp - t);
  116. else if (clock + timestamp > t + 3 * 1000)
  117. clock = t - timestamp;
  118. timestamp += diff;
  119. s_timestamp = timestamp > s_timestamp ? timestamp : s_timestamp;
  120. r = flv_parser_tag(type, dash->packet, taglen, timestamp, dash_live_onflv, dash);
  121. if (0 != r)
  122. {
  123. assert(0);
  124. break; // TODO: handle send failed
  125. }
  126. }
  127. flv_reader_destroy(f);
  128. diff = s_timestamp + 30;
  129. }
  130. }
  131. static int dash_server_mpd(http_session_t* session, dash_playlist_t* dash)
  132. {
  133. http_server_set_header(session, "Content-Type", "application/xml+dash");
  134. AutoThreadLocker locker(s_locker);
  135. http_server_reply(session, 200, dash->playlist, strlen(dash->playlist));
  136. return 0;
  137. }
  138. static int dash_server_onlive(void* dash, http_session_t* session, const char* /*method*/, const char* path)
  139. {
  140. char fullpath[1024];
  141. int r = path_concat(path + 6 /* /live/ */, LOCALPATH, fullpath);
  142. printf("live: %s\n", fullpath);
  143. // HTTP CORS
  144. http_server_set_header(session, "Access-Control-Allow-Origin", "*");
  145. http_server_set_header(session, "Access-Control-Allow-Headers", "*");
  146. http_server_set_header(session, "Access-Control-Allow-Credentials", "true");
  147. http_server_set_header(session, "Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS, CONNECT");
  148. const char* name = path_basename(fullpath);
  149. if (strendswith(name, ".mpd"))
  150. {
  151. return dash_server_mpd(session, (dash_playlist_t*)dash);
  152. }
  153. else if (path_testfile(name))
  154. {
  155. // cross domain
  156. return http_server_sendfile(session, name, NULL, NULL);
  157. }
  158. http_server_set_status_code(session, 404, NULL);
  159. return http_server_send(session, "", 0, NULL, NULL);
  160. }
  161. static int dash_server_onvod(void* /*dash*/, http_session_t* session, const char* /*method*/, const char* path)
  162. {
  163. char fullpath[1024];
  164. int r = path_concat(path + 5 /* /vod/ */, LOCALPATH, fullpath);
  165. printf("vod: %s\n", fullpath);
  166. // HTTP CORS
  167. http_server_set_header(session, "Access-Control-Allow-Origin", "*");
  168. http_server_set_header(session, "Access-Control-Allow-Headers", "*");
  169. http_server_set_header(session, "Access-Control-Allow-Credentials", "true");
  170. http_server_set_header(session, "Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS, CONNECT");
  171. if (0 == r && path_testfile(fullpath))
  172. {
  173. // MIME
  174. if (strendswith(fullpath, ".mpd"))
  175. http_server_set_content_type(session, "application/xml+dash");
  176. else if (strendswith(fullpath, ".mp4") || strendswith(fullpath, ".m4v"))
  177. http_server_set_header(session, "content-type", "video/mp4");
  178. else if (strendswith(fullpath, ".m4a"))
  179. http_server_set_header(session, "content-type", "audio/mp4");
  180. //http_server_set_header(session, "Transfer-Encoding", "chunked");
  181. return http_server_sendfile(session, fullpath, NULL, NULL);
  182. }
  183. http_server_set_status_code(session, 404, NULL);
  184. return http_server_send(session, "", 0, NULL, NULL);
  185. }
  186. void dash_dynamic_test(const char* ip, int port, const char* file, int width, int height)
  187. {
  188. std::shared_ptr<dash_playlist_t> live(new dash_playlist_t());
  189. live->mpd = dash_mpd_create(DASH_DYNAMIC, dash_mpd_onsegment, live.get());
  190. live->name = "live";
  191. live->width = width;
  192. live->height = height;
  193. live->adapation_audio = live->adapation_video = -1;
  194. aio_worker_init(4);
  195. http_server_t* http = http_server_create(ip, port);
  196. http_server_set_handler(http, http_server_route, live.get());
  197. http_server_addroute("/live/", dash_server_onlive);
  198. http_server_addroute("/vod/", dash_server_onvod);
  199. // live worker
  200. dash_live_worker(file, live.get());
  201. http_server_destroy(http);
  202. aio_worker_clean(4);
  203. dash_mpd_destroy(live->mpd);
  204. }