Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2005-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / MPEG Program Stream demuxer 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 : #ifndef GPAC_DISABLE_MPEG2PS
33 : #include "../media_tools/mpeg2_ps.h"
34 : #include <gpac/media_tools.h>
35 :
36 : typedef struct
37 : {
38 : GF_FilterPid *opid;
39 : u32 stream_type;
40 : u32 stream_num;
41 : Bool in_use;
42 : u64 last_dts;
43 : } M2PSStream;
44 :
45 :
46 : typedef struct
47 : {
48 : GF_FilterPid *ipid;
49 :
50 : const char *src_url;
51 : mpeg2ps_t *ps;
52 :
53 : Double start_range;
54 : u64 first_dts;
55 :
56 : u32 nb_playing;
57 : GF_Fraction64 duration;
58 : Bool in_seek;
59 :
60 : GF_List *streams;
61 : Bool initial_play_done;
62 : } GF_M2PSDmxCtx;
63 :
64 7 : static void m2psdmx_setup(GF_Filter *filter, GF_M2PSDmxCtx *ctx)
65 : {
66 : u32 i, nb_streams;
67 : u32 sync_id = 0;
68 : Double fps;
69 : GF_Fraction64 dur;
70 :
71 : dur.den = 1000;
72 7 : dur.num = (s32) mpeg2ps_get_max_time_msec(ctx->ps);
73 :
74 7 : ctx->first_dts = mpeg2ps_get_first_cts(ctx->ps);
75 :
76 7 : nb_streams = mpeg2ps_get_video_stream_count(ctx->ps);
77 12 : for (i=0; i<nb_streams; i++) {
78 : u32 par;
79 : u32 id;
80 : GF_Fraction frac;
81 : M2PSStream *st = NULL;
82 5 : u32 j, count = gf_list_count(ctx->streams);
83 5 : for (j=0; j<count; j++) {
84 0 : st = gf_list_get(ctx->streams, j);
85 0 : if ((st->stream_type==GF_STREAM_VISUAL) && !st->in_use) break;
86 : st = NULL;
87 : }
88 5 : if (!st) {
89 5 : GF_SAFEALLOC(st, M2PSStream);
90 5 : if (!st) continue;
91 5 : st->opid = gf_filter_pid_new(filter);
92 5 : st->stream_type = GF_STREAM_VISUAL;
93 5 : gf_list_add(ctx->streams, st);
94 : }
95 5 : st->in_use = GF_TRUE;
96 5 : st->stream_num = i;
97 5 : id = 0x100 | mpeg2ps_get_video_stream_id(ctx->ps, st->stream_num);
98 5 : if (!sync_id) sync_id = id;
99 :
100 5 : gf_filter_pid_copy_properties(st->opid, ctx->ipid);
101 5 : gf_filter_pid_set_name(st->opid, "Video");
102 5 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(st->stream_type) );
103 5 : switch (mpeg2ps_get_video_stream_type(ctx->ps, st->stream_num)) {
104 5 : case MPEG_VIDEO_MPEG1:
105 5 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_CODECID, &PROP_UINT(GF_CODECID_MPEG1) );
106 5 : break;
107 0 : case MPEG_VIDEO_MPEG2:
108 0 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_CODECID, &PROP_UINT(GF_CODECID_MPEG2_MAIN) );
109 0 : break;
110 : default:
111 : break;
112 : }
113 5 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_TIMESCALE, &PROP_UINT(90000) );
114 5 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_ID, &PROP_UINT( id) );
115 5 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_CLOCK_ID, &PROP_UINT( sync_id ) );
116 :
117 5 : fps = mpeg2ps_get_video_stream_framerate(ctx->ps, i);
118 5 : if (fps) {
119 5 : gf_media_get_video_timing(fps, &frac.num, &frac.den);
120 5 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_FPS, &PROP_FRAC( frac ) );
121 : }
122 5 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_WIDTH, &PROP_UINT( mpeg2ps_get_video_stream_width(ctx->ps, i) ) );
123 5 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_HEIGHT, &PROP_UINT( mpeg2ps_get_video_stream_height(ctx->ps, i) ) );
124 5 : par = mpeg2ps_get_video_stream_aspect_ratio(ctx->ps, i);
125 5 : if (par) {
126 0 : frac.num = par>>16;
127 0 : frac.den = (par&0xffff);
128 0 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_SAR, &PROP_FRAC( frac ) );
129 : }
130 5 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_DURATION, &PROP_FRAC64( dur ) );
131 5 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_UNFRAMED, &PROP_BOOL( GF_TRUE ) );
132 5 : gf_filter_pid_set_property_str(st->opid, "nocts", &PROP_BOOL(GF_TRUE ));
133 :
134 5 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_BITRATE, &PROP_UINT((u32) mpeg2ps_get_video_stream_bitrate(ctx->ps, i) ) );
135 :
136 5 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_PLAYBACK_MODE, &PROP_UINT(GF_PLAYBACK_MODE_FASTFORWARD ) );
137 : }
138 :
139 7 : nb_streams = mpeg2ps_get_audio_stream_count(ctx->ps);
140 14 : for (i=0; i<nb_streams; i++) {
141 : u32 id;
142 : char szName[20];
143 : M2PSStream *st = NULL;
144 7 : u32 j, count = gf_list_count(ctx->streams);
145 :
146 7 : if (mpeg2ps_get_audio_stream_type(ctx->ps, i) == MPEG_AUDIO_UNKNOWN) {
147 0 : continue;
148 : }
149 :
150 5 : for (j=0; j<count; j++) {
151 5 : st = gf_list_get(ctx->streams, j);
152 5 : if ((st->stream_type==GF_STREAM_AUDIO) && !st->in_use) break;
153 : st = NULL;
154 : }
155 7 : if (!st) {
156 7 : GF_SAFEALLOC(st, M2PSStream);
157 7 : if (!st) continue;
158 7 : st->opid = gf_filter_pid_new(filter);
159 7 : st->stream_type = GF_STREAM_AUDIO;
160 7 : gf_list_add(ctx->streams, st);
161 : }
162 7 : st->in_use = GF_TRUE;
163 7 : st->stream_num = i;
164 7 : id = 0x100 | mpeg2ps_get_audio_stream_id(ctx->ps, st->stream_num);
165 7 : if (!sync_id) sync_id = id;
166 :
167 7 : gf_filter_pid_copy_properties(st->opid, ctx->ipid);
168 7 : sprintf(szName, "Audio%d", i+1);
169 7 : gf_filter_pid_set_name(st->opid, szName);
170 7 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(st->stream_type) );
171 7 : switch (mpeg2ps_get_audio_stream_type(ctx->ps, st->stream_num)) {
172 5 : case MPEG_AUDIO_MPEG:
173 5 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_CODECID, &PROP_UINT( GF_CODECID_MPEG_AUDIO) );
174 5 : break;
175 2 : case MPEG_AUDIO_AC3:
176 2 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_CODECID, &PROP_UINT( GF_CODECID_AC3) );
177 2 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_UNFRAMED, &PROP_BOOL( GF_TRUE) );
178 2 : break;
179 0 : case MPEG_AUDIO_LPCM:
180 0 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_CODECID, &PROP_UINT(GF_4CC('L','P','C','M') ) );
181 0 : break;
182 : default:
183 : break;
184 : }
185 7 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_SAMPLE_RATE, &PROP_UINT( mpeg2ps_get_audio_stream_sample_freq(ctx->ps, i) ) );
186 7 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_NUM_CHANNELS, &PROP_UINT( mpeg2ps_get_audio_stream_channels(ctx->ps, i) ) );
187 7 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_BITRATE, &PROP_UINT( mpeg2ps_get_audio_stream_bitrate(ctx->ps, i) ) );
188 :
189 7 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_TIMESCALE, &PROP_UINT(90000) );
190 7 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_ID, &PROP_UINT( id) );
191 7 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_CLOCK_ID, &PROP_UINT( sync_id ) );
192 7 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_DURATION, &PROP_FRAC64( dur ) );
193 7 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_UNFRAMED, &PROP_BOOL( GF_TRUE ) );
194 7 : gf_filter_pid_set_property_str(st->opid, "nocts", &PROP_BOOL(GF_TRUE ));
195 :
196 7 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_PLAYBACK_MODE, &PROP_UINT(GF_PLAYBACK_MODE_FASTFORWARD ) );
197 : }
198 7 : }
199 :
200 :
201 7 : GF_Err m2psdmx_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
202 : {
203 : u32 i;
204 : const GF_PropertyValue *p;
205 7 : GF_M2PSDmxCtx *ctx = gf_filter_get_udta(filter);
206 :
207 7 : if (is_remove) {
208 0 : ctx->ipid = NULL;
209 0 : while (gf_list_count(ctx->streams) ) {
210 0 : M2PSStream *st = gf_list_pop_back(ctx->streams);
211 0 : if (st->opid)
212 0 : gf_filter_pid_remove(st->opid);
213 0 : gf_free(st);
214 : }
215 : return GF_OK;
216 : }
217 7 : if (! gf_filter_pid_check_caps(pid))
218 : return GF_NOT_SUPPORTED;
219 :
220 7 : if (!ctx->ipid) {
221 : GF_FilterEvent fevt;
222 7 : ctx->ipid = pid;
223 :
224 : //we work with full file only, send a play event on source to indicate that
225 7 : GF_FEVT_INIT(fevt, GF_FEVT_PLAY, pid);
226 : fevt.play.start_range = 0;
227 7 : fevt.base.on_pid = ctx->ipid;
228 7 : fevt.play.full_file_only = GF_TRUE;
229 7 : gf_filter_pid_send_event(ctx->ipid, &fevt);
230 : }
231 :
232 7 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILEPATH);
233 7 : if (!p) return GF_NOT_SUPPORTED;
234 :
235 7 : if (ctx->src_url && !strcmp(ctx->src_url, p->value.string)) return GF_OK;
236 :
237 7 : if (ctx->ps) {
238 0 : mpeg2ps_close(ctx->ps);
239 0 : for (i=0; i<gf_list_count(ctx->streams); i++) {
240 0 : M2PSStream *st = gf_list_get(ctx->streams, i);
241 0 : st->in_use = GF_FALSE;
242 : }
243 : }
244 7 : ctx->ps = NULL;
245 :
246 7 : ctx->src_url = p->value.string;
247 :
248 7 : return GF_OK;
249 : }
250 :
251 3388 : static Bool m2psdmx_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
252 : {
253 : u32 i;
254 3388 : GF_M2PSDmxCtx *ctx = gf_filter_get_udta(filter);
255 :
256 3388 : switch (evt->base.type) {
257 12 : case GF_FEVT_PLAY:
258 12 : if (ctx->nb_playing && (ctx->start_range == evt->play.start_range)) {
259 5 : ctx->nb_playing++;
260 5 : return GF_TRUE;
261 : }
262 7 : ctx->nb_playing++;
263 7 : ctx->start_range = evt->play.start_range;
264 7 : gf_filter_post_process_task(filter);
265 :
266 7 : if (!ctx->initial_play_done) {
267 7 : ctx->initial_play_done = GF_TRUE;
268 : //seek will not change the current source state, don't send a seek
269 7 : if (ctx->start_range<0.5)
270 : return GF_TRUE;
271 : }
272 :
273 2 : for (i=0; i<gf_list_count(ctx->streams); i++) {
274 2 : M2PSStream *pss = gf_list_get(ctx->streams, i);
275 2 : if (pss->opid == evt->base.on_pid)
276 1 : pss->in_use = GF_TRUE;
277 2 : if (!pss->in_use) continue;
278 :
279 2 : if (pss->stream_type==GF_STREAM_VISUAL) {
280 1 : mpeg2ps_seek_video_frame(ctx->ps, pss->stream_num, (u64) (ctx->start_range*1000));
281 : } else {
282 1 : mpeg2ps_seek_audio_frame(ctx->ps, pss->stream_num, (u64) (ctx->start_range*1000) );
283 : }
284 : }
285 : //cancel event
286 : return GF_TRUE;
287 :
288 3 : case GF_FEVT_STOP:
289 3 : ctx->nb_playing--;
290 8 : for (i=0; i<gf_list_count(ctx->streams); i++) {
291 5 : M2PSStream *pss = gf_list_get(ctx->streams, i);
292 5 : if (pss->opid == evt->base.on_pid)
293 3 : pss->in_use = GF_FALSE;
294 : }
295 : //cancel event if not last stream playing
296 3 : if (ctx->nb_playing) return GF_TRUE;
297 :
298 2 : return GF_FALSE;
299 :
300 : case GF_FEVT_SET_SPEED:
301 : //cancel event
302 : return GF_TRUE;
303 : default:
304 : break;
305 : }
306 : //by default don't cancel event - to rework once we have downloading in place
307 3373 : return GF_FALSE;
308 : }
309 :
310 9714 : GF_Err m2psdmx_process(GF_Filter *filter)
311 : {
312 9714 : GF_M2PSDmxCtx *ctx = gf_filter_get_udta(filter);
313 : Bool start, end;
314 : u32 i, count, nb_done;
315 9714 : if (!ctx->ps) {
316 3373 : GF_FilterPacket *pck = gf_filter_pid_get_packet(ctx->ipid);
317 3373 : if (!pck) {
318 : return GF_OK;
319 : }
320 3373 : gf_filter_pck_get_framing(pck, &start, &end);
321 3373 : if (!end) {
322 3366 : gf_filter_pid_drop_packet(ctx->ipid);
323 3366 : return GF_OK;
324 : }
325 7 : gf_filter_pid_drop_packet(ctx->ipid);
326 :
327 7 : ctx->ps = mpeg2ps_init(ctx->src_url);
328 7 : if (!ctx->ps) {
329 : GF_Err e = GF_NON_COMPLIANT_BITSTREAM;
330 0 : if (! gf_file_exists(ctx->src_url)) e = GF_URL_ERROR;
331 0 : gf_filter_setup_failure(filter, e);
332 0 : return GF_NOT_SUPPORTED;
333 : }
334 7 : m2psdmx_setup(filter, ctx);
335 : }
336 6348 : if (!ctx->nb_playing) return GF_OK;
337 :
338 :
339 : nb_done = 0;
340 6309 : count = gf_list_count(ctx->streams);
341 :
342 6309 : if (ctx->in_seek) {
343 0 : u64 seek_to = (u64) (1000*ctx->start_range);
344 0 : for (i=0; i<count;i++) {
345 0 : M2PSStream *st = gf_list_get(ctx->streams, i);
346 0 : if (!st->in_use) continue;
347 0 : if (st->stream_type==GF_STREAM_VISUAL) {
348 0 : mpeg2ps_seek_video_frame(ctx->ps, st->stream_num, seek_to);
349 : } else {
350 0 : mpeg2ps_seek_audio_frame(ctx->ps, st->stream_num, seek_to);
351 : }
352 : }
353 0 : ctx->in_seek = GF_FALSE;
354 : }
355 :
356 :
357 12585 : for (i=0; i<count;i++) {
358 : u8 *buf;
359 : u32 buf_len;
360 : u8 *pck_data;
361 : GF_FilterPacket *dst_pck;
362 12585 : M2PSStream *st = gf_list_get(ctx->streams, i);
363 12585 : if (!st->in_use) {
364 0 : nb_done++;
365 2871 : continue;
366 : }
367 :
368 12585 : if (gf_filter_pid_would_block(st->opid)) continue;
369 :
370 11626 : if (st->stream_type==GF_STREAM_VISUAL) {
371 : u8 ftype;
372 : u64 dts, cts;
373 5320 : u32 res = mpeg2ps_get_video_frame(ctx->ps, st->stream_num, (u8 **) &buf, &buf_len, &ftype, TS_90000, &dts, &cts);
374 5320 : if (!res) {
375 1495 : nb_done++;
376 2990 : continue;
377 : }
378 :
379 : //bug in some streams, make sure we don't dispatch twice the same ts
380 3825 : if (st->last_dts == dts) dts++;;
381 3825 : st->last_dts = dts;
382 :
383 3825 : if ((buf[buf_len - 4] == 0) && (buf[buf_len - 3] == 0) && (buf[buf_len - 2] == 1)) buf_len -= 4;
384 3825 : dst_pck = gf_filter_pck_new_alloc(st->opid, buf_len, &pck_data);
385 3825 : if (!dst_pck) continue;
386 :
387 3825 : memcpy(pck_data, buf, buf_len);
388 3825 : if (ftype==1) gf_filter_pck_set_sap(dst_pck, GF_FILTER_SAP_1);
389 :
390 3825 : if (cts != GF_FILTER_NO_TS) {
391 3825 : if (dts == GF_FILTER_NO_TS) dts = cts;
392 3825 : dts -= ctx->first_dts;
393 3825 : cts -= ctx->first_dts;
394 3825 : gf_filter_pck_set_dts(dst_pck, dts);
395 3825 : gf_filter_pck_set_cts(dst_pck, cts);
396 3825 : gf_filter_pck_set_framing(dst_pck, GF_TRUE, GF_FALSE);
397 : } else {
398 0 : gf_filter_pck_set_framing(dst_pck, GF_FALSE, GF_FALSE);
399 : }
400 3825 : gf_filter_pck_send(dst_pck);
401 : } else {
402 : u64 cts;
403 6306 : u32 res = mpeg2ps_get_audio_frame(ctx->ps, st->stream_num, (u8**)&buf, &buf_len, TS_90000, NULL, &cts);
404 6306 : if (!res) {
405 417 : nb_done++;
406 834 : continue;
407 : }
408 5889 : dst_pck = gf_filter_pck_new_alloc(st->opid, buf_len, &pck_data);
409 5889 : if (!dst_pck) continue;
410 :
411 5889 : memcpy(pck_data, buf, buf_len);
412 5889 : if (cts != GF_FILTER_NO_TS) {
413 5889 : cts -= ctx->first_dts;
414 5889 : gf_filter_pck_set_cts(dst_pck, cts);
415 5889 : gf_filter_pck_set_framing(dst_pck, GF_TRUE, GF_FALSE);
416 : } else {
417 0 : gf_filter_pck_set_framing(dst_pck, GF_FALSE, GF_FALSE);
418 : }
419 5889 : gf_filter_pck_set_sap(dst_pck, GF_FILTER_SAP_1);
420 :
421 5889 : gf_filter_pck_send(dst_pck);
422 : }
423 : }
424 :
425 6309 : if (nb_done==count) {
426 9 : for (i=0; i<count;i++) {
427 9 : M2PSStream *st = gf_list_get(ctx->streams, i);
428 9 : gf_filter_pid_set_eos(st->opid);
429 : }
430 : return GF_EOS;
431 : }
432 : return GF_OK;
433 : }
434 :
435 7 : GF_Err m2psdmx_initialize(GF_Filter *filter)
436 : {
437 7 : GF_M2PSDmxCtx *ctx = gf_filter_get_udta(filter);
438 7 : ctx->streams = gf_list_new();
439 7 : return GF_OK;
440 : }
441 :
442 7 : void m2psdmx_finalize(GF_Filter *filter)
443 : {
444 7 : GF_M2PSDmxCtx *ctx = gf_filter_get_udta(filter);
445 :
446 26 : while (gf_list_count(ctx->streams)) {
447 12 : M2PSStream *st = gf_list_pop_back(ctx->streams);
448 12 : gf_free(st);
449 : }
450 7 : gf_list_del(ctx->streams);
451 7 : if (ctx->ps) mpeg2ps_close(ctx->ps);
452 7 : }
453 :
454 3072 : static const char *m2psdmx_probe_data(const u8 *data, u32 size, GF_FilterProbeScore *score)
455 : {
456 3072 : *score = GF_FPROBE_EXT_MATCH;
457 3072 : return "mpg|mpeg|vob";
458 : }
459 :
460 : static const GF_FilterCapability M2PSDmxCaps[] =
461 : {
462 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
463 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "mpg|mpeg|vob"),
464 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "video/mpeg|audio/mpeg"),
465 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
466 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
467 : CAP_UINT(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_RAW),
468 : };
469 :
470 :
471 :
472 : GF_FilterRegister M2PSDmxRegister = {
473 : .name = "m2psdmx",
474 : GF_FS_SET_DESCRIPTION("MPEG PS demuxer")
475 : GF_FS_SET_HELP("This filter demultiplexes MPEG-2 program stream files/data to produce media PIDs and frames.")
476 : .private_size = sizeof(GF_M2PSDmxCtx),
477 : .initialize = m2psdmx_initialize,
478 : .finalize = m2psdmx_finalize,
479 : SETCAPS(M2PSDmxCaps),
480 : .configure_pid = m2psdmx_configure_pid,
481 : .process = m2psdmx_process,
482 : .process_event = m2psdmx_process_event,
483 : .probe_data = m2psdmx_probe_data,
484 : //this filter is not very reliable, prefer ffmpeg when available
485 : .priority = 255
486 : };
487 :
488 : #endif // GPAC_DISABLE_MPEG2PS
489 :
490 2877 : const GF_FilterRegister *m2psdmx_register(GF_FilterSession *session)
491 : {
492 : #ifndef GPAC_DISABLE_MPEG2PS
493 2877 : return &M2PSDmxRegister;
494 : #else
495 : return NULL;
496 : #endif
497 : }
498 :
|