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.

208 lines
4.8KB

  1. #include "hls-m3u8.h"
  2. #include "hls-param.h"
  3. #include "list.h"
  4. #include <inttypes.h>
  5. #include <errno.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <assert.h>
  10. #define VMAX(a, b) ((a) > (b) ? (a) : (b))
  11. struct hls_m3u8_t
  12. {
  13. int live;
  14. int version;
  15. int64_t seq; // m3u8 sequence number (base 0)
  16. int64_t duration;// target duration
  17. size_t count;
  18. struct list_head root;
  19. char* ext_x_map;
  20. };
  21. struct hls_segment_t
  22. {
  23. struct list_head link;
  24. int64_t pts; // present timestamp (millisecond)
  25. int64_t duration; // segment duration (millisecond)
  26. int64_t offset; // EXT-X-BYTERANGE offset: a byte offset from the beginning of the resource
  27. int64_t bytes; // EXT-X-BYTERANGE length
  28. int discontinuity; // EXT-X-DISCONTINUITY flag
  29. char* name;
  30. size_t capacity;
  31. };
  32. struct hls_m3u8_t* hls_m3u8_create(int live, int version)
  33. {
  34. struct hls_m3u8_t* m3u8;
  35. m3u8 = (struct hls_m3u8_t*)calloc(1, sizeof(*m3u8));
  36. if (NULL == m3u8)
  37. return NULL;
  38. assert(0 == live || live >= HLS_LIVE_NUM);
  39. m3u8->version = version;
  40. m3u8->live = live;
  41. m3u8->seq = 0;
  42. m3u8->count = 0;
  43. m3u8->duration = 0;
  44. LIST_INIT_HEAD(&m3u8->root);
  45. return m3u8;
  46. }
  47. void hls_m3u8_destroy(struct hls_m3u8_t* m3u8)
  48. {
  49. struct list_head* l, *n;
  50. struct hls_segment_t* seg;
  51. list_for_each_safe(l, n, &m3u8->root)
  52. {
  53. seg = list_entry(l, struct hls_segment_t, link);
  54. free(seg);
  55. }
  56. if (m3u8->ext_x_map)
  57. free(m3u8->ext_x_map);
  58. free(m3u8);
  59. }
  60. static struct hls_segment_t* hls_segment_alloc(size_t bytes)
  61. {
  62. struct hls_segment_t* seg;
  63. seg = (struct hls_segment_t*)malloc(sizeof(*seg) + bytes);
  64. if (seg)
  65. {
  66. seg->name = (char*)(seg + 1);
  67. seg->capacity = bytes;
  68. seg->offset = 0;
  69. seg->bytes = 0;
  70. seg->pts = 0;
  71. seg->discontinuity = 0;
  72. }
  73. return seg;
  74. }
  75. int hls_m3u8_add(struct hls_m3u8_t* m3u8, const char* name, int64_t pts, int64_t duration, int discontinuity)
  76. {
  77. return hls_m3u8_add_with_offset(m3u8, name, pts, duration, discontinuity, 0, 0);
  78. }
  79. int hls_m3u8_add_with_offset(hls_m3u8_t* m3u8, const char* name, int64_t pts, int64_t duration, int discontinuity, int64_t offset, int64_t bytes)
  80. {
  81. size_t r;
  82. struct hls_segment_t* seg;
  83. seg = NULL;
  84. r = strlen(name);
  85. if (0 != m3u8->live && m3u8->count >= (size_t)m3u8->live)
  86. {
  87. assert(m3u8->count == (size_t)m3u8->live);
  88. ++m3u8->seq; // update EXT-X-MEDIA-SEQUENCE
  89. // reuse the first segment
  90. seg = list_entry(m3u8->root.next, struct hls_segment_t, link);
  91. list_remove(&seg->link);
  92. // check name length
  93. if (r + 1 > seg->capacity)
  94. {
  95. free(seg);
  96. seg = NULL;
  97. --m3u8->count;
  98. }
  99. }
  100. if (NULL == seg)
  101. {
  102. // reserve more space for reuse segment
  103. seg = hls_segment_alloc(r + (m3u8->live ? 16 : 1));
  104. if (!seg)
  105. return -ENOMEM;
  106. ++m3u8->count;
  107. }
  108. // update EXT-X-TARGETDURATION
  109. m3u8->duration = VMAX(m3u8->duration, duration);
  110. // segment
  111. seg->pts = pts;
  112. seg->bytes = bytes;
  113. seg->offset = offset;
  114. seg->duration = duration;
  115. seg->discontinuity = discontinuity; // EXT-X-DISCONTINUITY
  116. memcpy(seg->name, name, r + 1); // copy last '\0'
  117. list_insert_after(&seg->link, m3u8->root.prev);
  118. return 0;
  119. }
  120. int hls_m3u8_set_x_map(hls_m3u8_t* m3u8, const char* name)
  121. {
  122. if (m3u8->ext_x_map)
  123. free(m3u8->ext_x_map);
  124. m3u8->ext_x_map = name ? strdup(name) : NULL;
  125. return m3u8->ext_x_map ? 0 : -ENOMEM;
  126. }
  127. size_t hls_m3u8_count(struct hls_m3u8_t* m3u8)
  128. {
  129. return m3u8->count;
  130. }
  131. int hls_m3u8_playlist(struct hls_m3u8_t* m3u8, int eof, char* playlist, size_t bytes)
  132. {
  133. int r;
  134. size_t n;
  135. struct list_head* link;
  136. struct hls_segment_t* seg;
  137. r = snprintf(playlist, bytes,
  138. "#EXTM3U\n" // MUST
  139. "#EXT-X-VERSION:%d\n" // Optional
  140. "#EXT-X-TARGETDURATION:%" PRId64 "\n" // MUST, decimal-integer, in seconds
  141. "#EXT-X-MEDIA-SEQUENCE:%" PRId64 "\n"
  142. "%s" // #EXT-X-PLAYLIST-TYPE:VOD
  143. "%s", // #EXT-X-ALLOW-CACHE:YES
  144. m3u8->version,
  145. (m3u8->duration + 999) / 1000,
  146. m3u8->seq,
  147. m3u8->live ? "" : "#EXT-X-PLAYLIST-TYPE:VOD\n",
  148. m3u8->live ? "" : "#EXT-X-ALLOW-CACHE:YES\n");
  149. if (r <= 0 || (size_t)r >= bytes)
  150. return -ENOMEM;
  151. // #EXT-X-MAP:URI="main.mp4",BYTERANGE="1206@0"
  152. if (m3u8->ext_x_map)
  153. r += snprintf(playlist + r, r < bytes ? bytes - r : 0, "#EXT-X-MAP:URI=\"%s\",\n", m3u8->ext_x_map);
  154. n = r;
  155. list_for_each(link, &m3u8->root)
  156. {
  157. if (bytes <= n)
  158. break;
  159. seg = list_entry(link, struct hls_segment_t, link);
  160. if (seg->discontinuity)
  161. n += snprintf(playlist + n, n < bytes ? bytes - n : 0, "#EXT-X-DISCONTINUITY\n");
  162. if (bytes > n)
  163. {
  164. if(seg->bytes > 0)
  165. n += snprintf(playlist + n, n < bytes ? bytes - n : 0, "#EXTINF:%.3f,\n#EXT-X-BYTERANGE:%"PRIu64"@%"PRIu64"\n%s\n", seg->duration / 1000.0, seg->bytes, seg->offset, seg->name);
  166. else
  167. n += snprintf(playlist + n, n < bytes ? bytes - n : 0, "#EXTINF:%.3f,\n%s\n", seg->duration / 1000.0, seg->name);
  168. }
  169. }
  170. if (eof && bytes > n + 15)
  171. n += snprintf(playlist + n, n < bytes ? bytes - n : 0, "#EXT-X-ENDLIST\n");
  172. return (bytes > n && n > 0) ? 0 : -ENOMEM;
  173. }