mptsd reads mpegts streams from udp/multicast or http and combines them into one multiple program stream that is suitable for outputting to DVB-C modulator. Tested with Dektec DTE-3114 Quad QAM Modulator and used in production in small DVB-C networks. https://georgi.unixsol.org/programs/mptsd/

output_write.c 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. /*
  2. * mptsd output writing
  3. * Copyright (C) 2010-2011 Unix Solutions Ltd.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License version 2
  7. * as published by the Free Software Foundation.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  17. */
  18. #include <unistd.h>
  19. #include <string.h>
  20. #include <signal.h>
  21. #include <sys/time.h>
  22. #include <errno.h>
  23. #include <math.h>
  24. #include "libfuncs/io.h"
  25. #include "libfuncs/log.h"
  26. #include "libfuncs/list.h"
  27. #include "libtsfuncs/tsfuncs.h"
  28. #include "sleep.h"
  29. #include "data.h"
  30. #include "config.h"
  31. #include "network.h"
  32. void increase_process_priority() {
  33. return;
  34. #ifdef __linux__
  35. struct sched_param param;
  36. param.sched_priority = 99;
  37. if (sched_setscheduler(0, SCHED_FIFO, &param)==-1) {
  38. log_perror("sched_setscheduler() failed!", errno);
  39. } else {
  40. LOGf("PRIO : sched_setschedule() succeded.\n");
  41. }
  42. #endif
  43. }
  44. void ts_frame_process(CONFIG *conf, OUTPUT *o, uint8_t *data) {
  45. int i;
  46. uint16_t pid;
  47. uint8_t *ts_packet;
  48. for (i=0; i<FRAME_PACKET_SIZE; i+=TS_PACKET_SIZE) {
  49. ts_packet = data + i;
  50. pid = ts_packet_get_pid(ts_packet);
  51. if (pid == 0x1fff) // NULL packet
  52. o->padding_period += TS_PACKET_SIZE;
  53. if (ts_packet_has_pcr(ts_packet)) {
  54. uint64_t pcr = ts_packet_get_pcr(ts_packet); // Current PCR
  55. uint64_t new_pcr = pcr;
  56. uint64_t bytes = o->traffic + i;
  57. if (o->last_pcr[pid]) {
  58. uint64_t old_pcr = o->last_pcr[pid];
  59. uint64_t old_org_pcr = o->last_org_pcr[pid];
  60. uint64_t old_bytes = o->last_traffic[pid];
  61. if (old_org_pcr < pcr) { // Detect PCR wraparound
  62. new_pcr = old_pcr + (double)((bytes - old_bytes) * 8 * 27000000) / o->output_bitrate;
  63. // Rewrite pcrs || Move pcrs & rewrite prcs
  64. if (conf->pcr_mode == 2 || conf->pcr_mode == 3) {
  65. ts_packet_set_pcr(ts_packet, new_pcr);
  66. }
  67. if (conf->debug) {
  68. uint64_t ts_rate = (double)(((bytes - old_bytes) * 8) * 27000000) / (pcr - old_org_pcr);
  69. uint64_t ts_rate_new = (double)(((bytes - old_bytes) * 8) * 27000000) / (new_pcr - old_pcr);
  70. LOGf("PCR[%03x]: old:%14llu new:%14llu pcr_diff:%8lld ts_rate:%9llu ts_rate_new:%9llu diff:%9lld | passed:%llu\n",
  71. pid,
  72. pcr,
  73. new_pcr,
  74. pcr - new_pcr,
  75. ts_rate,
  76. ts_rate_new,
  77. ts_rate - ts_rate_new,
  78. bytes - old_bytes
  79. );
  80. }
  81. }
  82. } else {
  83. // if (config->debug) {
  84. // LOGf("PCR[%03x]: %10llu init\n", pid, pcr);
  85. // }
  86. }
  87. o->last_pcr[pid] = new_pcr;
  88. o->last_org_pcr[pid] = pcr;
  89. o->last_traffic[pid] = bytes;
  90. }
  91. }
  92. }
  93. ssize_t ts_frame_write(OUTPUT *o, uint8_t *data) {
  94. ssize_t written;
  95. written = fdwrite(o->out_sock, (char *)data, FRAME_PACKET_SIZE);
  96. if (written >= 0) {
  97. o->traffic += written;
  98. o->traffic_period += written;
  99. }
  100. if (o->ofd)
  101. write(o->ofd, data, FRAME_PACKET_SIZE);
  102. return written;
  103. }
  104. void * output_handle_write(void *_config) {
  105. CONFIG *conf = _config;
  106. OUTPUT *o = conf->output;
  107. int buf_in_use = 0;
  108. unsigned int o_datasize = 0;
  109. struct timeval stats_ts, now;
  110. struct timeval start_write_ts, end_write_ts, used_ts;
  111. unsigned long long stats_interval;
  112. signal(SIGPIPE, SIG_IGN);
  113. increase_process_priority();
  114. gettimeofday(&stats_ts, NULL);
  115. while (!o->dienow) {
  116. gettimeofday(&now, NULL);
  117. OBUF *curbuf = &o->obuf[buf_in_use];
  118. while (curbuf->status != obuf_full) { // Wait untill the buffer is ready ot it is already emptying
  119. if (o->dienow)
  120. goto OUT;
  121. //LOGf("MIX: Waiting for obuf %d\n", buf_in_use);
  122. usleep(1);
  123. }
  124. curbuf->status = obuf_emptying; // Mark buffer as being filled
  125. // Show stats
  126. stats_interval = timeval_diff_msec(&stats_ts, &now);
  127. if (stats_interval > conf->timeouts.stats) {
  128. stats_ts = now;
  129. double out_kbps = (double)(o->traffic_period * 8) / 1000;
  130. double out_mbps = (double)out_kbps / 1000;
  131. double opadding = ((double)o->padding_period / o->traffic_period) * 100;
  132. if (!conf->quiet) {
  133. LOGf("STAT : Pad:%6.2f%% Traf:%5.2f Mbps | %8.2f | %7llu\n",
  134. opadding,
  135. out_mbps,
  136. out_kbps,
  137. o->traffic_period
  138. );
  139. }
  140. o->traffic_period = 0;
  141. o->padding_period = 0;
  142. o_datasize = 0;
  143. }
  144. gettimeofday(&start_write_ts, NULL);
  145. int packets_written = 0, real_sleep_time = conf->output_tmout - conf->usleep_overhead;
  146. long time_taken, time_diff, real_time, overhead = 0, overhead_total = 0;
  147. ssize_t written = 0;
  148. while (curbuf->written < curbuf->size) {
  149. if (o->dienow)
  150. goto OUT;
  151. long sleep_interval = conf->output_tmout;
  152. uint8_t *ts_frame = curbuf->buf + curbuf->written;
  153. ts_frame_process(conf, o, ts_frame); // Fix PCR and count NULL packets
  154. written += ts_frame_write(o, ts_frame); // Write packet to network/file
  155. curbuf->written += FRAME_PACKET_SIZE;
  156. if (packets_written) {
  157. time_taken = timeval_diff_usec(&start_write_ts, &used_ts);
  158. real_time = packets_written * (conf->output_tmout + conf->usleep_overhead);
  159. time_diff = real_time - time_taken;
  160. overhead = (time_taken / packets_written) - sleep_interval;
  161. overhead_total += overhead;
  162. /*
  163. LOGf("[%5d] time_taken:%5ld real_time:%5ld time_diff:%ld | overhead:%5ld overhead_total:%5ld\n",
  164. packets_written,
  165. time_taken,
  166. real_time,
  167. time_diff,
  168. overhead,
  169. overhead_total
  170. );
  171. */
  172. if (time_diff > real_sleep_time) {
  173. sleep_interval = time_diff - conf->usleep_overhead;
  174. if (sleep_interval < 0)
  175. sleep_interval = 1;
  176. // LOGf("Add sleep. time_diff: %ld sleep_interval: %ld\n", time_diff, sleep_interval);
  177. } else {
  178. //LOGf("Skip sleep %ld\n", time_diff);
  179. sleep_interval = 0;
  180. }
  181. }
  182. if (sleep_interval > 0)
  183. usleep(sleep_interval);
  184. gettimeofday(&used_ts, NULL);
  185. packets_written++;
  186. }
  187. gettimeofday(&end_write_ts, NULL);
  188. unsigned long long write_time = timeval_diff_usec(&start_write_ts, &end_write_ts);
  189. if (write_time < o->obuf_ms * 1000) {
  190. //LOGf("Writen for -%llu us less\n", o->obuf_ms*1000 - write_time);
  191. usleep(o->obuf_ms*1000 - write_time);
  192. } else {
  193. //LOGf("Writen for +%llu us more\n", write_time - o->obuf_ms*1000);
  194. }
  195. obuf_reset(curbuf); // Buffer us all used up
  196. buf_in_use = buf_in_use ? 0 : 1; // Switch buffer
  197. if (written < 0) {
  198. LOG("OUTPUT: Error writing into output socket.\n");
  199. shutdown_fd(&o->out_sock);
  200. connect_output(o);
  201. }
  202. }
  203. OUT:
  204. LOG("OUTPUT: WRITE thread stopped.\n");
  205. o->dienow++;
  206. return 0;
  207. }