Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom Paris 2019-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / MPEG Transport Stream splitter filter
9 : *
10 : * GPAC is free software; you can redistribute it and/or modify
11 : * it under the terms of the GNU Lesser General Public License as published by
12 : * the Free Software Foundation; either version 2, or (at your option)
13 : * any later version.
14 : *
15 : * GPAC is distributed in the hope that it will be useful,
16 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : * GNU Lesser General Public License for more details.
19 : *
20 : * You should have received a copy of the GNU Lesser General Public
21 : * License along with this library; see the file COPYING. If not, write to
22 : * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 : *
24 : */
25 :
26 : #include <gpac/filters.h>
27 : #include <gpac/constants.h>
28 : #include <gpac/thread.h>
29 : #include <gpac/list.h>
30 : #include <gpac/bitstream.h>
31 :
32 : #include <gpac/mpegts.h>
33 :
34 : typedef struct
35 : {
36 : GF_FilterPid *opid;
37 : u32 pmt_pid;
38 : u8 pat_pck[192];
39 : u32 pat_pck_size;
40 :
41 : u8 *pck_buffer;
42 : u32 nb_pck;
43 : } GF_M2TSSplit_SPTS;
44 :
45 :
46 : typedef struct
47 : {
48 : //options
49 : Bool dvb;
50 : s32 mux_id;
51 : Bool avonly;
52 : u32 nb_pack;
53 :
54 : //internal
55 : GF_Filter *filter;
56 : GF_FilterPid *ipid;
57 :
58 : GF_List *streams;
59 :
60 : GF_M2TS_Demuxer *dmx;
61 :
62 : u8 tsbuf[192];
63 : GF_BitStream *bsw;
64 :
65 : } GF_M2TSSplitCtx;
66 :
67 :
68 :
69 141926 : void m2tssplit_send_packet(GF_M2TSSplitCtx *ctx, GF_M2TSSplit_SPTS *stream, u8 *data, u32 size)
70 : {
71 : u8 *buffer;
72 : GF_FilterPacket *pck;
73 141926 : if (ctx->nb_pack) {
74 : assert (stream->nb_pck<ctx->nb_pack);
75 141926 : if (data) {
76 141920 : memcpy(stream->pck_buffer + size*stream->nb_pck, data, size);
77 141920 : stream->nb_pck++;
78 141920 : if (stream->nb_pck<ctx->nb_pack) {
79 127732 : return;
80 : }
81 : }
82 14194 : u32 osize = size*stream->nb_pck;
83 14194 : pck = gf_filter_pck_new_alloc(stream->opid, osize, &buffer);
84 14194 : if (pck) {
85 14194 : gf_filter_pck_set_framing(pck, GF_FALSE, GF_FALSE);
86 14194 : memcpy(buffer, stream->pck_buffer, osize);
87 14194 : gf_filter_pck_send(pck);
88 : }
89 14194 : stream->nb_pck = 0;
90 : } else {
91 0 : pck = gf_filter_pck_new_alloc(stream->opid, size, &buffer);
92 0 : if (pck) {
93 0 : gf_filter_pck_set_framing(pck, GF_FALSE, GF_FALSE);
94 0 : memcpy(buffer, data, size);
95 0 : gf_filter_pck_send(pck);
96 : }
97 : }
98 : }
99 :
100 14250 : void m2tssplit_flush(GF_M2TSSplitCtx *ctx)
101 : {
102 : u32 i;
103 14250 : if (!ctx->nb_pack) return;
104 :
105 99750 : for (i=0; i<gf_list_count(ctx->streams); i++ ) {
106 99750 : GF_M2TSSplit_SPTS *stream = gf_list_get(ctx->streams, i);
107 99750 : if (stream->opid && stream->nb_pck)
108 6 : m2tssplit_send_packet(ctx, stream, NULL, 0);
109 : }
110 :
111 : }
112 :
113 2 : GF_Err m2tssplit_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
114 : {
115 2 : GF_M2TSSplitCtx *ctx = gf_filter_get_udta(filter);
116 :
117 2 : if (is_remove) {
118 0 : ctx->ipid = NULL;
119 0 : m2tssplit_flush(ctx);
120 0 : while (gf_list_count(ctx->streams) ) {
121 0 : GF_M2TSSplit_SPTS *st = gf_list_pop_back(ctx->streams);
122 0 : if (st->opid) gf_filter_pid_remove(st->opid);
123 0 : if (st->pck_buffer) gf_free(st->pck_buffer);
124 0 : gf_free(st);
125 : }
126 : return GF_OK;
127 : }
128 2 : if (! gf_filter_pid_check_caps(pid))
129 : return GF_NOT_SUPPORTED;
130 :
131 2 : ctx->ipid = pid;
132 2 : return GF_OK;
133 : }
134 :
135 5646 : static Bool m2tssplit_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
136 : {
137 : // GF_M2TSSplitCtx *ctx = gf_filter_get_udta(filter);
138 :
139 5646 : switch (evt->base.type) {
140 : case GF_FEVT_PLAY:
141 : //cancel event
142 : return GF_TRUE;
143 :
144 0 : case GF_FEVT_STOP:
145 0 : return GF_FALSE;
146 :
147 : case GF_FEVT_SET_SPEED:
148 : //cancel event
149 : return GF_TRUE;
150 : default:
151 : break;
152 : }
153 : //by default don't cancel event - to rework once we have downloading in place
154 5640 : return GF_FALSE;
155 : }
156 :
157 19890 : GF_Err m2tssplit_process(GF_Filter *filter)
158 : {
159 19890 : GF_M2TSSplitCtx *ctx = gf_filter_get_udta(filter);
160 : GF_FilterPacket *pck;
161 : const u8 *data;
162 : u32 data_size;
163 19890 : pck = gf_filter_pid_get_packet(ctx->ipid);
164 19890 : if (!pck) {
165 14250 : if (gf_filter_pid_is_eos(ctx->ipid))
166 14250 : m2tssplit_flush(ctx);
167 : return GF_OK;
168 : }
169 5640 : data = gf_filter_pck_get_data(pck, &data_size);
170 5640 : if (data) {
171 5640 : gf_m2ts_process_data(ctx->dmx, (u8 *)data, data_size);
172 : }
173 5640 : gf_filter_pid_drop_packet(ctx->ipid);
174 5640 : return GF_OK;
175 : }
176 :
177 :
178 151307 : void m2tssplit_on_event(struct tag_m2ts_demux *ts, u32 evt_type, void *par)
179 : {
180 : u32 i;
181 151307 : GF_M2TSSplitCtx *ctx = ts->user;
182 :
183 151307 : if ((evt_type==GF_M2TS_EVT_PAT_FOUND) || (evt_type==GF_M2TS_EVT_PAT_UPDATE)) {
184 : //todo, purge previous programs if PMT PID changes
185 15 : for (i=0; i<gf_list_count(ctx->dmx->programs); i++) {
186 : GF_M2TS_SectionInfo *sinfo = par;
187 : GF_M2TSSplit_SPTS *stream=NULL;
188 : u32 j, pck_size, tot_len=188, crc, offset=5, mux_id;
189 : u8 *buffer;
190 : Bool first_pck=GF_FALSE;
191 7 : GF_M2TS_Program *prog = gf_list_get(ctx->dmx->programs, i);
192 : assert(prog->pmt_pid);
193 :
194 28 : for (j=0; j<gf_list_count(ctx->streams); j++) {
195 21 : stream = gf_list_get(ctx->streams, j);
196 21 : if (stream->pmt_pid==prog->pmt_pid)
197 : break;
198 : stream = NULL;
199 : }
200 7 : if (!stream) {
201 7 : GF_SAFEALLOC(stream, GF_M2TSSplit_SPTS);
202 7 : if (!stream) return;
203 7 : stream->pmt_pid = prog->pmt_pid;
204 : first_pck = GF_TRUE;
205 7 : prog->user = stream;
206 7 : if (ctx->nb_pack)
207 7 : stream->pck_buffer = gf_malloc(sizeof(char) * ctx->nb_pack * (ctx->dmx->prefix_present ? 192 : 188) );
208 :
209 7 : gf_list_add(ctx->streams, stream);
210 :
211 : //do not create output stream until we are sure we have AV component in program
212 : }
213 :
214 : //generate a pat
215 7 : gf_bs_seek(ctx->bsw, 0);
216 7 : if (ctx->dmx->prefix_present) {
217 : tot_len += 4;
218 : offset += 4;
219 0 : gf_bs_write_u32(ctx->bsw, 1);
220 : }
221 7 : gf_bs_write_u8(ctx->bsw, 0x47);
222 7 : gf_bs_write_int(ctx->bsw, 0, 1); // error indicator
223 7 : gf_bs_write_int(ctx->bsw, 1, 1); // start ind
224 7 : gf_bs_write_int(ctx->bsw, 0, 1); // transport priority
225 7 : gf_bs_write_int(ctx->bsw, 0, 13); // pid
226 7 : gf_bs_write_int(ctx->bsw, 0, 2); // scrambling
227 7 : gf_bs_write_int(ctx->bsw, 1, 2); // we do not use adaptation field for sections
228 7 : gf_bs_write_int(ctx->bsw, 0, 4); // continuity counter
229 7 : gf_bs_write_u8(ctx->bsw, 0); // ptr_field
230 7 : gf_bs_write_int(ctx->bsw, GF_M2TS_TABLE_ID_PAT, 8);
231 7 : gf_bs_write_int(ctx->bsw, 1/*use_syntax_indicator*/, 1);
232 7 : gf_bs_write_int(ctx->bsw, 0, 1);
233 7 : gf_bs_write_int(ctx->bsw, 3, 2); /* reserved bits are all set */
234 7 : gf_bs_write_int(ctx->bsw, 5 + 4 + 4, 12); //syntax indicator section + PAT payload + CRC32
235 : /*syntax indicator section*/
236 :
237 7 : if (ctx->mux_id>=0) {
238 0 : mux_id = ctx->mux_id;
239 : } else {
240 : //we use muxID*255 + streamIndex as the target mux ID if not configured
241 7 : mux_id = sinfo->ex_table_id;
242 7 : mux_id *= 255;
243 : }
244 7 : mux_id += i;
245 :
246 7 : gf_bs_write_int(ctx->bsw, mux_id, 16);
247 7 : gf_bs_write_int(ctx->bsw, 3, 2); /* reserved bits are all set */
248 7 : gf_bs_write_int(ctx->bsw, sinfo->version_number, 5);
249 7 : gf_bs_write_int(ctx->bsw, 1, 1); /* current_next_indicator = 1: we don't send version in advance */
250 7 : gf_bs_write_int(ctx->bsw, 0, 8); //current section number
251 7 : gf_bs_write_int(ctx->bsw, 0, 8); //last section number
252 :
253 : /*PAT payload*/
254 7 : gf_bs_write_u16(ctx->bsw, prog->number);
255 7 : gf_bs_write_int(ctx->bsw, 0x7, 3); /*reserved*/
256 7 : gf_bs_write_int(ctx->bsw, prog->pmt_pid, 13); /*reserved*/
257 :
258 7 : pck_size = (u32) gf_bs_get_position(ctx->bsw);
259 : /*write CRC32 starting from first field in section until last before CRC*/
260 7 : crc = gf_crc_32(ctx->tsbuf + offset, pck_size - offset);
261 7 : ctx->tsbuf[pck_size] = (crc >> 24) & 0xFF;
262 7 : ctx->tsbuf[pck_size+1] = (crc >> 16) & 0xFF;
263 7 : ctx->tsbuf[pck_size+2] = (crc >> 8) & 0xFF;
264 7 : ctx->tsbuf[pck_size+3] = crc & 0xFF;
265 7 : pck_size += 4;
266 :
267 : //pad the rest to 0xFF
268 7 : memset(ctx->tsbuf + pck_size, 0xFF, tot_len - pck_size);
269 : //copy over for PAT repeat
270 7 : memcpy(stream->pat_pck, ctx->tsbuf, tot_len);
271 7 : stream->pat_pck_size = tot_len;
272 :
273 : //output new PAT
274 7 : if (stream->opid) {
275 0 : GF_FilterPacket *pck = gf_filter_pck_new_alloc(stream->opid, tot_len, &buffer);
276 0 : if (pck) {
277 0 : gf_filter_pck_set_framing(pck, first_pck, GF_FALSE);
278 0 : memcpy(buffer, ctx->tsbuf, tot_len);
279 0 : gf_filter_pck_send(pck);
280 : }
281 : }
282 : }
283 : return;
284 : }
285 : //resend PAT
286 151306 : if (evt_type==GF_M2TS_EVT_PAT_REPEAT) {
287 630 : for (i=0; i<gf_list_count(ctx->dmx->programs); i++) {
288 : u8 *buffer;
289 630 : GF_M2TS_Program *prog = gf_list_get(ctx->dmx->programs, i);
290 630 : GF_M2TSSplit_SPTS *stream = prog->user;
291 720 : if (!stream) continue;
292 630 : if (!stream->opid) continue;
293 :
294 540 : GF_FilterPacket *pck = gf_filter_pck_new_alloc(stream->opid, stream->pat_pck_size, &buffer);
295 540 : if (pck) {
296 540 : gf_filter_pck_set_framing(pck, GF_FALSE, GF_FALSE);
297 540 : memcpy(buffer, stream->pat_pck, stream->pat_pck_size);
298 540 : gf_filter_pck_send(pck);
299 : }
300 : }
301 : return;
302 : }
303 151216 : if ((evt_type==GF_M2TS_EVT_PMT_FOUND) || (evt_type==GF_M2TS_EVT_PMT_UPDATE)) {
304 : GF_M2TS_Program *prog = par;
305 7 : GF_M2TSSplit_SPTS *stream = prog->user;
306 : u32 known_streams = 0;
307 35 : for (i=0; i<gf_list_count(prog->streams); i++) {
308 28 : GF_M2TS_ES *es = gf_list_get(prog->streams, i);
309 28 : switch (es->stream_type) {
310 13 : case GF_M2TS_VIDEO_MPEG1:
311 : case GF_M2TS_VIDEO_MPEG2:
312 : case GF_M2TS_VIDEO_DCII:
313 : case GF_M2TS_VIDEO_MPEG4:
314 : case GF_M2TS_VIDEO_H264:
315 : case GF_M2TS_VIDEO_SVC:
316 : case GF_M2TS_VIDEO_HEVC:
317 : case GF_M2TS_VIDEO_HEVC_TEMPORAL:
318 : case GF_M2TS_VIDEO_HEVC_MCTS:
319 : case GF_M2TS_VIDEO_SHVC:
320 : case GF_M2TS_VIDEO_SHVC_TEMPORAL:
321 : case GF_M2TS_VIDEO_MHVC:
322 : case GF_M2TS_VIDEO_MHVC_TEMPORAL:
323 : case GF_M2TS_VIDEO_VVC:
324 : case GF_M2TS_VIDEO_VVC_TEMPORAL:
325 : case GF_M2TS_VIDEO_VC1:
326 : case GF_M2TS_AUDIO_MPEG1:
327 : case GF_M2TS_AUDIO_MPEG2:
328 : case GF_M2TS_AUDIO_LATM_AAC:
329 : case GF_M2TS_AUDIO_AAC:
330 : case GF_CODECID_AAC_MPEG2_MP:
331 : case GF_CODECID_AAC_MPEG2_LCP:
332 : case GF_CODECID_AAC_MPEG2_SSRP:
333 : case GF_M2TS_AUDIO_AC3:
334 : case GF_M2TS_AUDIO_EC3:
335 : case GF_M2TS_SYSTEMS_MPEG4_SECTIONS:
336 : case GF_M2TS_SYSTEMS_MPEG4_PES:
337 : case GF_M2TS_METADATA_PES:
338 : case GF_M2TS_METADATA_ID3_HLS:
339 13 : known_streams++;
340 13 : break;
341 : default:
342 : break;
343 : }
344 : }
345 : //if avonly mode and no known streams, do not forward program
346 7 : if (ctx->avonly && !known_streams)
347 : return;
348 : //good to go, create output and send PAT
349 6 : if (!stream->opid) {
350 : u8 *buffer;
351 :
352 6 : stream->opid = gf_filter_pid_new(ctx->filter);
353 6 : gf_filter_pid_set_property(stream->opid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE));
354 6 : gf_filter_pid_set_property(stream->opid, GF_PROP_PID_MIME, &PROP_STRING("video/mpeg-2"));
355 6 : gf_filter_pid_set_property(stream->opid, GF_PROP_PID_FILE_EXT, &PROP_STRING("ts"));
356 6 : gf_filter_pid_set_property(stream->opid, GF_PROP_PID_SERVICE_ID, &PROP_UINT(prog->number));
357 :
358 6 : GF_FilterPacket *pck = gf_filter_pck_new_alloc(stream->opid, stream->pat_pck_size, &buffer);
359 6 : if (pck) {
360 6 : gf_filter_pck_set_framing(pck, GF_TRUE, GF_FALSE);
361 6 : memcpy(buffer, stream->pat_pck, stream->pat_pck_size);
362 6 : gf_filter_pck_send(pck);
363 : }
364 : }
365 : return;
366 : }
367 :
368 151209 : if (evt_type==GF_M2TS_EVT_PCK) {
369 : GF_M2TS_TSPCK *tspck = par;
370 : Bool do_fwd;
371 149859 : GF_M2TSSplit_SPTS *stream = tspck->stream ? tspck->stream->program->user : NULL;
372 :
373 141063 : if (stream) {
374 141063 : if (!stream->opid) return;
375 :
376 140852 : if (ctx->dmx->prefix_present) {
377 0 : u8 *data = tspck->data;
378 0 : data -= 4;
379 0 : m2tssplit_send_packet(ctx, stream, data, 192);
380 : } else {
381 140852 : m2tssplit_send_packet(ctx, stream, tspck->data, 188);
382 : }
383 : return;
384 : }
385 :
386 8796 : if (!ctx->dvb) return;
387 :
388 : do_fwd = GF_FALSE;
389 8796 : switch (tspck->pid) {
390 : case GF_M2TS_PID_CAT:
391 : case GF_M2TS_PID_TSDT:
392 : case GF_M2TS_PID_NIT_ST:
393 : case GF_M2TS_PID_SDT_BAT_ST:
394 : case GF_M2TS_PID_EIT_ST_CIT:
395 : case GF_M2TS_PID_RST_ST:
396 : case GF_M2TS_PID_TDT_TOT_ST:
397 : case GF_M2TS_PID_NET_SYNC:
398 : case GF_M2TS_PID_RNT:
399 : case GF_M2TS_PID_IN_SIG:
400 : case GF_M2TS_PID_MEAS:
401 : case GF_M2TS_PID_DIT:
402 : case GF_M2TS_PID_SIT:
403 : do_fwd = GF_TRUE;
404 : }
405 : if (do_fwd) {
406 178 : u32 count = gf_list_count(ctx->streams);
407 1424 : for (i=0; i<count; i++) {
408 1246 : stream = gf_list_get(ctx->streams, i);
409 1246 : if (!stream->opid) continue;
410 :
411 1068 : if (ctx->dmx->prefix_present) {
412 0 : u8 *data = tspck->data;
413 0 : data -= 4;
414 0 : m2tssplit_send_packet(ctx, stream, data, 192);
415 : } else {
416 1068 : m2tssplit_send_packet(ctx, stream, tspck->data, 188);
417 : }
418 : }
419 : }
420 : }
421 : }
422 :
423 1 : GF_Err m2tssplit_initialize(GF_Filter *filter)
424 : {
425 1 : GF_M2TSSplitCtx *ctx = gf_filter_get_udta(filter);
426 1 : ctx->streams = gf_list_new();
427 1 : ctx->dmx = gf_m2ts_demux_new();
428 1 : ctx->dmx->on_event = m2tssplit_on_event;
429 1 : ctx->dmx->split_mode = GF_TRUE;
430 1 : ctx->dmx->user = ctx;
431 1 : ctx->filter = filter;
432 1 : ctx->bsw = gf_bs_new(ctx->tsbuf, 192, GF_BITSTREAM_WRITE);
433 1 : if (ctx->nb_pack<=1)
434 0 : ctx->nb_pack = 0;
435 1 : return GF_OK;
436 : }
437 :
438 1 : void m2tssplit_finalize(GF_Filter *filter)
439 : {
440 1 : GF_M2TSSplitCtx *ctx = gf_filter_get_udta(filter);
441 :
442 9 : while (gf_list_count(ctx->streams)) {
443 7 : GF_M2TSSplit_SPTS *st = gf_list_pop_back(ctx->streams);
444 7 : if (st->pck_buffer) gf_free(st->pck_buffer);
445 7 : gf_free(st);
446 : }
447 1 : gf_list_del(ctx->streams);
448 1 : gf_bs_del(ctx->bsw);
449 1 : gf_m2ts_demux_del(ctx->dmx);
450 1 : }
451 :
452 : static const GF_FilterCapability M2TSSplitCaps[] =
453 : {
454 : CAP_UINT(GF_CAPS_INPUT_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
455 : CAP_STRING(GF_CAPS_INPUT_OUTPUT, GF_PROP_PID_FILE_EXT, "ts|m2t|mts|dmb|trp"),
456 : CAP_STRING(GF_CAPS_INPUT_OUTPUT, GF_PROP_PID_MIME, "video/mpeg-2|video/mp2t|video/mpeg"),
457 : };
458 :
459 : #define OFFS(_n) #_n, offsetof(GF_M2TSSplitCtx, _n)
460 : static const GF_FilterArgs M2TSSplitArgs[] =
461 : {
462 : { OFFS(dvb), "forward all packets from global DVB PIDs", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_ADVANCED},
463 : { OFFS(mux_id), "set initial ID of output mux; the first program will use mux_id, the second mux_id+1, etc. If not set, this value will be set to sourceMuxId*255", GF_PROP_SINT, "-1", NULL, GF_FS_ARG_HINT_EXPERT},
464 : { OFFS(avonly), "do not forward programs with no AV component", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_ADVANCED},
465 : { OFFS(nb_pack), "pack N packets before sending", GF_PROP_UINT, "10", NULL, GF_FS_ARG_HINT_ADVANCED},
466 :
467 : {0}
468 : };
469 :
470 :
471 :
472 : GF_FilterRegister M2TSSplitRegister = {
473 : .name = "tssplit",
474 : GF_FS_SET_DESCRIPTION("MPEG Transport Stream splitter")
475 : GF_FS_SET_HELP("This filter splits an MPEG-2 transport stream into several single program transport streams.\n"
476 : "Only the PAT table is rewritten, the CAT table, PMT and all program streams are forwarded as is.\n"
477 : "In [-full]() mode, global DVB tables of the input multiplex are forwarded to each output mux; otherwise these tables are discarded.")
478 : .flags = GF_FS_REG_EXPLICIT_ONLY,
479 : .private_size = sizeof(GF_M2TSSplitCtx),
480 : .initialize = m2tssplit_initialize,
481 : .finalize = m2tssplit_finalize,
482 : .args = M2TSSplitArgs,
483 : SETCAPS(M2TSSplitCaps),
484 : .configure_pid = m2tssplit_configure_pid,
485 : .process = m2tssplit_process,
486 : .process_event = m2tssplit_process_event,
487 : };
488 :
489 2877 : const GF_FilterRegister *m2tssplit_register(GF_FilterSession *session)
490 : {
491 2877 : return &M2TSSplitRegister;
492 : }
493 :
|