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.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  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. struct timeval stats_ts, now;
  109. struct timeval start_write_ts, end_write_ts, used_ts;
  110. unsigned long long stats_interval;
  111. signal(SIGPIPE, SIG_IGN);
  112. increase_process_priority();
  113. gettimeofday(&stats_ts, NULL);
  114. while (!o->dienow) {
  115. gettimeofday(&now, NULL);
  116. OBUF *curbuf = &o->obuf[buf_in_use];
  117. while (curbuf->status != obuf_full) { // Wait untill the buffer is ready ot it is already emptying
  118. if (o->dienow)
  119. goto OUT;
  120. //LOGf("MIX: Waiting for obuf %d\n", buf_in_use);
  121. usleep(1);
  122. }
  123. curbuf->status = obuf_emptying; // Mark buffer as being filled
  124. // Show stats
  125. stats_interval = timeval_diff_msec(&stats_ts, &now);
  126. if (stats_interval > conf->timeouts.stats) {
  127. stats_ts = now;
  128. double out_kbps = (double)(o->traffic_period * 8) / 1000;
  129. double out_mbps = (double)out_kbps / 1000;
  130. double opadding = ((double)o->padding_period / o->traffic_period) * 100;
  131. if (!conf->quiet) {
  132. LOGf("STAT : Pad:%6.2f%% Traf:%5.2f Mbps | %8.2f | %7llu\n",
  133. opadding,
  134. out_mbps,
  135. out_kbps,
  136. o->traffic_period
  137. );
  138. }
  139. o->traffic_period = 0;
  140. o->padding_period = 0;
  141. }
  142. gettimeofday(&start_write_ts, NULL);
  143. int packets_written = 0, real_sleep_time = conf->output_tmout - conf->usleep_overhead;
  144. long time_taken, time_diff, real_time, overhead = 0, overhead_total = 0;
  145. ssize_t written = 0;
  146. while (curbuf->written < curbuf->size) {
  147. if (o->dienow)
  148. goto OUT;
  149. long sleep_interval = conf->output_tmout;
  150. uint8_t *ts_frame = curbuf->buf + curbuf->written;
  151. ts_frame_process(conf, o, ts_frame); // Fix PCR and count NULL packets
  152. written += ts_frame_write(o, ts_frame); // Write packet to network/file
  153. curbuf->written += FRAME_PACKET_SIZE;
  154. if (packets_written) {
  155. time_taken = timeval_diff_usec(&start_write_ts, &used_ts);
  156. real_time = packets_written * (conf->output_tmout + conf->usleep_overhead);
  157. time_diff = real_time - time_taken;
  158. overhead = (time_taken / packets_written) - sleep_interval;
  159. overhead_total += overhead;
  160. /*
  161. LOGf("[%5d] time_taken:%5ld real_time:%5ld time_diff:%ld | overhead:%5ld overhead_total:%5ld\n",
  162. packets_written,
  163. time_taken,
  164. real_time,
  165. time_diff,
  166. overhead,
  167. overhead_total
  168. );
  169. */
  170. if (time_diff > real_sleep_time) {
  171. sleep_interval = time_diff - conf->usleep_overhead;
  172. if (sleep_interval < 0)
  173. sleep_interval = 1;
  174. // LOGf("Add sleep. time_diff: %ld sleep_interval: %ld\n", time_diff, sleep_interval);
  175. } else {
  176. //LOGf("Skip sleep %ld\n", time_diff);
  177. sleep_interval = 0;
  178. }
  179. }
  180. if (sleep_interval > 0)
  181. usleep(sleep_interval);
  182. gettimeofday(&used_ts, NULL);
  183. packets_written++;
  184. }
  185. gettimeofday(&end_write_ts, NULL);
  186. unsigned long long write_time = timeval_diff_usec(&start_write_ts, &end_write_ts);
  187. if (write_time < o->obuf_ms * 1000) {
  188. //LOGf("Writen for -%llu us less\n", o->obuf_ms*1000 - write_time);
  189. usleep(o->obuf_ms*1000 - write_time);
  190. } else {
  191. //LOGf("Writen for +%llu us more\n", write_time - o->obuf_ms*1000);
  192. }
  193. obuf_reset(curbuf); // Buffer us all used up
  194. buf_in_use = buf_in_use ? 0 : 1; // Switch buffer
  195. if (written < 0) {
  196. LOG("OUTPUT: Error writing into output socket.\n");
  197. shutdown_fd(&o->out_sock);
  198. connect_output(o);
  199. }
  200. }
  201. OUT:
  202. LOG("OUTPUT: WRITE thread stopped.\n");
  203. o->dienow++;
  204. return 0;
  205. }