Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2020-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / MHAS reframer 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/avparse.h>
27 : #include <gpac/constants.h>
28 : #include <gpac/filters.h>
29 :
30 :
31 : static u32 USACSampleRates[] = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0, 0,
32 : 57600, 51200, 40000, 38400, 34150, 28800, 25600, 20000, 19200, 17075, 14400, 12800, 9600};
33 :
34 : static u32 nb_usac_sr = GF_ARRAY_LENGTH(USACSampleRates);
35 :
36 :
37 : typedef struct
38 : {
39 : u64 pos;
40 : Double duration;
41 : } MHASIdx;
42 :
43 : typedef struct
44 : {
45 : //filter args
46 : Double index;
47 : Bool mpha;
48 : u32 pcksync;
49 : Bool nosync;
50 :
51 : //only one input pid declared
52 : GF_FilterPid *ipid;
53 : //only one output pid declared
54 : GF_FilterPid *opid;
55 :
56 : GF_BitStream *bs;
57 : u64 file_pos, cts, prev_cts;
58 :
59 : GF_Fraction64 duration;
60 : Double start_range;
61 : Bool in_seek;
62 : u32 timescale;
63 : Bool is_playing;
64 : Bool is_file;
65 : Bool initial_play_done, file_loaded;
66 :
67 : Bool initialized;
68 :
69 : u8 *mhas_buffer;
70 : u32 mhas_buffer_size, mhas_buffer_alloc, resume_from;
71 : u64 byte_offset;
72 : Bool buffer_too_small;
73 :
74 : GF_FilterPacket *src_pck;
75 :
76 : Bool recompute_cts;
77 : MHASIdx *indexes;
78 : u32 index_alloc_size, index_size;
79 :
80 : u32 sample_rate, frame_len, PL;
81 : s32 cicp_layout_idx, num_speakers;
82 : u32 nb_frames;
83 :
84 : u32 nb_unknown_pck;
85 : u32 bitrate;
86 : } GF_MHASDmxCtx;
87 :
88 :
89 :
90 :
91 5 : GF_Err mhas_dmx_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
92 : {
93 : const GF_PropertyValue *p;
94 5 : GF_MHASDmxCtx *ctx = gf_filter_get_udta(filter);
95 :
96 5 : if (is_remove) {
97 0 : ctx->ipid = NULL;
98 0 : if (ctx->opid) {
99 0 : gf_filter_pid_remove(ctx->opid);
100 0 : ctx->opid = NULL;
101 : }
102 : return GF_OK;
103 : }
104 5 : if (! gf_filter_pid_check_caps(pid))
105 : return GF_NOT_SUPPORTED;
106 :
107 5 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_TIMESCALE);
108 5 : if (p) {
109 0 : ctx->timescale = p->value.uint;
110 : //if stream comes from TS or other muxed source unframed, force initial sync check
111 0 : if (!ctx->ipid) {
112 0 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_UNFRAMED);
113 0 : if (p && p->value.boolean)
114 0 : ctx->nosync = GF_TRUE;
115 : }
116 : }
117 5 : ctx->ipid = pid;
118 5 : p = gf_filter_pid_get_property_str(pid, "nocts");
119 5 : if (p && p->value.boolean) ctx->recompute_cts = GF_TRUE;
120 5 : else ctx->recompute_cts = GF_FALSE;
121 :
122 5 : if (ctx->timescale && !ctx->opid) {
123 0 : ctx->opid = gf_filter_pid_new(filter);
124 0 : gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
125 0 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_UNFRAMED, NULL);
126 : }
127 : return GF_OK;
128 : }
129 :
130 1358 : static void mhas_dmx_check_dur(GF_Filter *filter, GF_MHASDmxCtx *ctx)
131 : {
132 : GF_Fraction64 duration;
133 : FILE *stream;
134 : GF_BitStream *bs;
135 : u32 frame_len, cur_dur;
136 : Bool mhas_sap;
137 : u64 mhas_last_cfg, rate;
138 : const GF_PropertyValue *p;
139 1358 : if (!ctx->opid || ctx->timescale || ctx->file_loaded) return;
140 :
141 5 : if (ctx->index<=0) {
142 2 : ctx->file_loaded = GF_TRUE;
143 : return;
144 : }
145 :
146 3 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILEPATH);
147 3 : if (!p || !p->value.string || !strncmp(p->value.string, "gmem://", 7)) {
148 0 : ctx->is_file = GF_FALSE;
149 0 : ctx->file_loaded = GF_TRUE;
150 : return;
151 : }
152 3 : ctx->is_file = GF_TRUE;
153 :
154 3 : stream = gf_fopen(p->value.string, "rb");
155 3 : if (!stream) return;
156 :
157 3 : ctx->index_size = 0;
158 :
159 3 : bs = gf_bs_from_file(stream, GF_BITSTREAM_READ);
160 : duration.num = duration.den = 0;
161 : frame_len = cur_dur = 0;
162 : mhas_last_cfg = 0;
163 :
164 3 : while (gf_bs_available(bs)) {
165 3 : u32 sync_code = gf_bs_peek_bits(bs, 24, 0);
166 3 : if (sync_code == 0xC001A5) {
167 : break;
168 : }
169 0 : gf_bs_skip_bytes(bs, 1);
170 : }
171 8028 : while (gf_bs_available(bs)) {
172 : u64 mhas_pck_start, pay_start, parse_end, mhas_size;
173 : u32 mhas_type;
174 :
175 8025 : mhas_pck_start = gf_bs_get_position(bs);
176 8025 : mhas_type = (u32) gf_mpegh_escaped_value(bs, 3, 8, 8);
177 8025 : /*mhas_label = */gf_mpegh_escaped_value(bs, 2, 8, 32);
178 8025 : mhas_size = gf_mpegh_escaped_value(bs, 11, 24, 24);
179 :
180 8025 : pay_start = (u32) gf_bs_get_position(bs);
181 :
182 8025 : if (!gf_bs_available(bs) ) break;
183 8025 : if (mhas_size > gf_bs_available(bs)) break;
184 :
185 : mhas_sap = 0;
186 : //frame
187 8025 : if (mhas_type==2) {
188 3873 : mhas_sap = gf_bs_read_int(bs, 1);
189 3873 : if (!mhas_last_cfg) mhas_sap = 0;
190 : //config
191 4152 : } else if (mhas_type==1) {
192 : u32 sr = 0;
193 93 : /*u32 pl = */gf_bs_read_u8(bs);
194 93 : u32 idx = gf_bs_read_int(bs, 5);
195 93 : if (idx==0x1f)
196 0 : duration.den = gf_bs_read_int(bs, 24);
197 93 : else if (sr < nb_usac_sr) {
198 93 : duration.den = USACSampleRates[idx];
199 : }
200 93 : idx = gf_bs_read_int(bs, 3);
201 93 : if ((idx==0) || (idx==2) ) frame_len = 768;
202 : else frame_len = 1024;
203 :
204 : mhas_last_cfg = mhas_pck_start;
205 : }
206 : //audio truncation
207 4059 : else if (mhas_type==17) {
208 0 : Bool isActive = gf_bs_read_int(bs, 1);
209 0 : /*Bool ati_reserved = */gf_bs_read_int(bs, 1);
210 0 : Bool trunc_from_begin = gf_bs_read_int(bs, 1);
211 0 : u32 nb_trunc_samples = gf_bs_read_int(bs, 13);
212 0 : if (isActive && !trunc_from_begin) {
213 0 : duration.num -= nb_trunc_samples;
214 : }
215 : }
216 8025 : gf_bs_align(bs);
217 8025 : parse_end = (u32) gf_bs_get_position(bs) - pay_start;
218 : //remaining of packet payload
219 8025 : gf_bs_skip_bytes(bs, mhas_size - parse_end);
220 :
221 : //mhas_sap only set for frames
222 8025 : if (mhas_sap && duration.den && (cur_dur >= ctx->index * duration.den) ) {
223 45 : if (!ctx->index_alloc_size) ctx->index_alloc_size = 10;
224 42 : else if (ctx->index_alloc_size == ctx->index_size) ctx->index_alloc_size *= 2;
225 45 : ctx->indexes = gf_realloc(ctx->indexes, sizeof(MHASIdx)*ctx->index_alloc_size);
226 45 : ctx->indexes[ctx->index_size].pos = mhas_last_cfg;
227 45 : ctx->indexes[ctx->index_size].duration = ((Double) duration.num) / duration.den;
228 45 : ctx->index_size ++;
229 : cur_dur = 0;
230 : }
231 8025 : if (mhas_type==2) {
232 3873 : duration.num += frame_len;
233 3873 : cur_dur += frame_len;
234 : mhas_last_cfg = 0;
235 : }
236 : }
237 :
238 3 : rate = gf_bs_get_position(bs);
239 3 : gf_bs_del(bs);
240 3 : gf_fclose(stream);
241 :
242 3 : if (!ctx->duration.num || (ctx->duration.num * duration.den != duration.num * ctx->duration.den)) {
243 3 : ctx->duration = duration;
244 3 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, & PROP_FRAC64(ctx->duration));
245 :
246 3 : if (duration.num && !gf_sys_is_test_mode() ) {
247 0 : rate *= 8 * ctx->duration.den;
248 0 : rate /= ctx->duration.num;
249 0 : ctx->bitrate = (u32) rate;
250 : }
251 : }
252 :
253 3 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILE_CACHED);
254 3 : if (p && p->value.boolean) ctx->file_loaded = GF_TRUE;
255 3 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CAN_DATAREF, & PROP_BOOL(GF_TRUE ) );
256 : }
257 :
258 142 : static void mhas_dmx_check_pid(GF_Filter *filter, GF_MHASDmxCtx *ctx, u32 PL, u32 sample_rate, u32 frame_len, s32 CICPspeakerLayoutIdx, s32 numSpeakers, u8 *dsi, u32 dsi_size)
259 : {
260 : u32 nb_channels;
261 : u64 chan_layout;
262 142 : if (!ctx->opid) {
263 5 : ctx->opid = gf_filter_pid_new(filter);
264 5 : mhas_dmx_check_dur(filter, ctx);
265 : } else {
266 137 : if ((ctx->frame_len == frame_len)
267 137 : && (ctx->PL == PL)
268 137 : && (ctx->sample_rate == sample_rate)
269 137 : && (ctx->cicp_layout_idx == CICPspeakerLayoutIdx)
270 137 : && (ctx->num_speakers == numSpeakers)
271 : ) {
272 : return;
273 : }
274 : }
275 5 : ctx->frame_len = frame_len;
276 5 : ctx->PL = PL;
277 5 : ctx->sample_rate = sample_rate;
278 5 : ctx->cicp_layout_idx = CICPspeakerLayoutIdx;
279 5 : ctx->num_speakers = numSpeakers;
280 :
281 : chan_layout = 0;
282 : nb_channels = 0;
283 5 : if (CICPspeakerLayoutIdx>=0) {
284 5 : chan_layout = gf_audio_fmt_get_layout_from_cicp(CICPspeakerLayoutIdx);
285 5 : nb_channels = gf_audio_fmt_get_num_channels_from_layout(chan_layout);
286 0 : } else if (numSpeakers>=0) {
287 0 : nb_channels = numSpeakers;
288 : }
289 :
290 : //copy properties at init or reconfig
291 5 : gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
292 5 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, & PROP_UINT( GF_STREAM_AUDIO));
293 5 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_UNFRAMED, NULL );
294 5 : if (ctx->is_file && ctx->index) {
295 3 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PLAYBACK_MODE, & PROP_UINT(GF_PLAYBACK_MODE_FASTFORWARD) );
296 : }
297 5 : if (ctx->duration.num)
298 3 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, & PROP_FRAC64(ctx->duration));
299 :
300 5 : if (!ctx->timescale) gf_filter_pid_set_name(ctx->opid, "audio");
301 :
302 5 : if (ctx->mpha) {
303 2 : u8 *data = gf_malloc(sizeof(u8) * (dsi_size+5) );
304 2 : if (!data) return;
305 2 : data[0] = 1;
306 2 : data[1] = PL;
307 2 : data[2] = CICPspeakerLayoutIdx;
308 2 : data[3] = dsi_size>>8;
309 2 : data[4] = dsi_size&0xFF;
310 2 : memcpy(data+5, dsi, dsi_size);
311 2 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, & PROP_UINT( GF_CODECID_MPHA ) );
312 2 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DECODER_CONFIG, & PROP_DATA_NO_COPY( data, (dsi_size+5) ) );
313 : } else {
314 3 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, & PROP_UINT( GF_CODECID_MHAS ) );
315 : }
316 :
317 5 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_TIMESCALE, & PROP_UINT(ctx->timescale ? ctx->timescale : ctx->sample_rate));
318 5 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLE_RATE, & PROP_UINT(ctx->sample_rate));
319 5 : if (chan_layout)
320 5 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CHANNEL_LAYOUT, & PROP_LONGUINT(chan_layout) );
321 5 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_NUM_CHANNELS, & PROP_UINT(nb_channels) );
322 5 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLES_PER_FRAME, & PROP_UINT(ctx->frame_len) );
323 :
324 5 : if (ctx->bitrate) {
325 0 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_BITRATE, & PROP_UINT(ctx->bitrate));
326 : }
327 : }
328 :
329 5290 : static Bool mhas_dmx_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
330 : {
331 : u32 i;
332 : GF_FilterEvent fevt;
333 5290 : GF_MHASDmxCtx *ctx = gf_filter_get_udta(filter);
334 :
335 5290 : if (evt->base.on_pid != ctx->opid) return GF_TRUE;
336 :
337 6 : switch (evt->base.type) {
338 5 : case GF_FEVT_PLAY:
339 5 : if (!ctx->is_playing) {
340 5 : ctx->is_playing = GF_TRUE;
341 : }
342 5 : if (! ctx->is_file) {
343 2 : if (evt->play.start_range || ctx->initial_play_done) {
344 0 : ctx->mhas_buffer_size = 0;
345 0 : ctx->resume_from = 0;
346 : }
347 2 : ctx->initial_play_done = GF_TRUE;
348 2 : return GF_FALSE;
349 : }
350 3 : mhas_dmx_check_dur(filter, ctx);
351 :
352 3 : ctx->start_range = evt->play.start_range;
353 3 : ctx->in_seek = GF_TRUE;
354 3 : ctx->file_pos = 0;
355 3 : if (ctx->start_range) {
356 0 : for (i=1; i<ctx->index_size; i++) {
357 0 : if (ctx->indexes[i].duration>ctx->start_range) {
358 0 : ctx->cts = (u64) (ctx->indexes[i-1].duration * ctx->sample_rate);
359 0 : ctx->file_pos = ctx->indexes[i-1].pos;
360 0 : break;
361 : }
362 : }
363 : }
364 3 : if (!ctx->initial_play_done) {
365 3 : ctx->initial_play_done = GF_TRUE;
366 : //seek will not change the current source state, don't send a seek
367 3 : if (!ctx->file_pos)
368 : return GF_TRUE;
369 : }
370 0 : ctx->mhas_buffer_size = 0;
371 0 : ctx->resume_from = 0;
372 : //post a seek
373 0 : GF_FEVT_INIT(fevt, GF_FEVT_SOURCE_SEEK, ctx->ipid);
374 0 : fevt.seek.start_offset = ctx->file_pos;
375 0 : gf_filter_pid_send_event(ctx->ipid, &fevt);
376 :
377 : //cancel event
378 0 : return GF_TRUE;
379 :
380 1 : case GF_FEVT_STOP:
381 1 : ctx->is_playing = GF_FALSE;
382 1 : if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
383 1 : ctx->src_pck = NULL;
384 : //don't cancel event
385 1 : return GF_FALSE;
386 :
387 : case GF_FEVT_SET_SPEED:
388 : //cancel event
389 : return GF_TRUE;
390 : default:
391 : break;
392 : }
393 : //by default don't cancel event - to rework once we have downloading in place
394 0 : return GF_FALSE;
395 : }
396 :
397 : static GFINLINE void mhas_dmx_update_cts(GF_MHASDmxCtx *ctx)
398 : {
399 5190 : if (ctx->timescale) {
400 0 : u64 inc = ctx->frame_len;
401 0 : inc *= ctx->timescale;
402 0 : inc /= ctx->sample_rate;
403 0 : ctx->cts += inc;
404 : } else {
405 5190 : ctx->cts += ctx->frame_len;
406 : }
407 : }
408 :
409 : #ifndef GPAC_DISABLE_LOG
410 0 : static const char *mhas_pck_name(u32 pck_type)
411 : {
412 0 : switch (pck_type) {
413 : case 0: return "FILL_DATA";
414 0 : case 1: return "MPEGH3DACFG";
415 0 : case 2: return "MPEGH3DAFRAME";
416 0 : case 3: return "AUDIOSCENEINFO";
417 0 : case 6: return "SYNC";
418 0 : case 7: return "SYNCGAP";
419 0 : case 8: return "MARKER";
420 0 : case 9: return "CRC16";
421 0 : case 10: return "CRC32";
422 0 : case 11: return "DESCRIPTOR";
423 0 : case 12: return "USERINTERACTION";
424 0 : case 13: return "LOUDNESS_DRC";
425 0 : case 14: return "BUFFERINFO";
426 0 : case 15: return "GLOBAL_CRC16";
427 0 : case 16: return "GLOBAL_CRC32";
428 0 : case 17: return "AUDIOTRUNCATION";
429 0 : case 18: return "GENDATA";
430 0 : case 4:
431 : case 5:
432 : default:
433 0 : return "ISOReserved";
434 : }
435 : return "error";
436 : }
437 : #endif
438 :
439 5387 : GF_Err mhas_dmx_process(GF_Filter *filter)
440 : {
441 5387 : GF_MHASDmxCtx *ctx = gf_filter_get_udta(filter);
442 : GF_FilterPacket *in_pck;
443 : u8 *output;
444 : u8 *start;
445 : Bool final_flush=GF_FALSE;
446 : u32 pck_size, remain, prev_pck_size;
447 : u64 cts = GF_FILTER_NO_TS;
448 : u32 au_start = 0;
449 : u32 consumed = 0;
450 : u32 nb_trunc_samples = 0;
451 : Bool trunc_from_begin = 0;
452 : Bool has_cfg = 0;
453 :
454 : //always reparse duration
455 5387 : if (!ctx->duration.num)
456 1350 : mhas_dmx_check_dur(filter, ctx);
457 :
458 5387 : if (ctx->opid && !ctx->is_playing)
459 : return GF_OK;
460 :
461 5367 : in_pck = gf_filter_pid_get_packet(ctx->ipid);
462 5367 : if (!in_pck) {
463 84 : if (gf_filter_pid_is_eos(ctx->ipid)) {
464 84 : if (!ctx->mhas_buffer_size) {
465 4 : if (ctx->opid)
466 4 : gf_filter_pid_set_eos(ctx->opid);
467 4 : if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
468 4 : ctx->src_pck = NULL;
469 4 : return GF_EOS;
470 : }
471 : final_flush = GF_TRUE;
472 0 : } else if (!ctx->resume_from) {
473 : return GF_OK;
474 : }
475 : }
476 :
477 5363 : prev_pck_size = ctx->mhas_buffer_size;
478 5363 : if (ctx->resume_from)
479 : in_pck = NULL;
480 :
481 197 : if (in_pck) {
482 197 : u8 *data = (u8 *) gf_filter_pck_get_data(in_pck, &pck_size);
483 :
484 197 : if (ctx->byte_offset != GF_FILTER_NO_BO) {
485 197 : u64 byte_offset = gf_filter_pck_get_byte_offset(in_pck);
486 197 : if (!ctx->mhas_buffer_size) {
487 5 : ctx->byte_offset = byte_offset;
488 192 : } else if (ctx->byte_offset + ctx->mhas_buffer_size != byte_offset) {
489 0 : ctx->byte_offset = GF_FILTER_NO_BO;
490 0 : if ((byte_offset != GF_FILTER_NO_BO) && (byte_offset>ctx->mhas_buffer_size) ) {
491 0 : ctx->byte_offset = byte_offset - ctx->mhas_buffer_size;
492 : }
493 : }
494 : }
495 :
496 197 : if (ctx->mhas_buffer_size + pck_size > ctx->mhas_buffer_alloc) {
497 21 : ctx->mhas_buffer_alloc = ctx->mhas_buffer_size + pck_size;
498 21 : ctx->mhas_buffer = gf_realloc(ctx->mhas_buffer, ctx->mhas_buffer_alloc);
499 : }
500 197 : memcpy(ctx->mhas_buffer + ctx->mhas_buffer_size, data, pck_size);
501 197 : ctx->mhas_buffer_size += pck_size;
502 : }
503 :
504 : //input pid sets some timescale - we flushed pending data , update cts
505 5363 : if (ctx->timescale && in_pck) {
506 0 : cts = gf_filter_pck_get_cts(in_pck);
507 : }
508 :
509 0 : if (cts == GF_FILTER_NO_TS) {
510 : //avoids updating cts
511 : prev_pck_size = 0;
512 : }
513 :
514 5363 : remain = ctx->mhas_buffer_size;
515 5363 : start = ctx->mhas_buffer;
516 :
517 5363 : if (ctx->resume_from) {
518 5166 : start += ctx->resume_from - 1;
519 5166 : remain -= ctx->resume_from - 1;
520 5166 : ctx->resume_from = 0;
521 : }
522 :
523 5363 : while (ctx->nosync && (remain>3)) {
524 : //wait till we have a frame header
525 5 : u8 *hdr_start = memchr(start, 0xC0, remain);
526 5 : if (!hdr_start) {
527 : remain=0;
528 : break;
529 : }
530 5 : if ((hdr_start[1]==0x01) && (hdr_start[2]==0xA5)) {
531 5 : GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[MHASDmx] Sync found !\n"));
532 5 : ctx->nosync = GF_FALSE;
533 5 : break;
534 : }
535 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[MHASDmx] not sync, skipping byte\n"));
536 0 : start++;
537 0 : remain--;
538 : }
539 5363 : if (ctx->nosync)
540 : goto skip;
541 :
542 5363 : gf_bs_reassign_buffer(ctx->bs, start, remain);
543 5363 : ctx->buffer_too_small = GF_FALSE;
544 :
545 : //MHAS packet
546 16543 : while (remain > consumed) {
547 : u32 pay_start, parse_end, mhas_size, mhas_label;
548 : Bool mhas_sap = 0;
549 : u32 mhas_type;
550 11180 : if (!ctx->is_playing && ctx->opid) {
551 5 : ctx->resume_from = 1;
552 : consumed = 0;
553 : break;
554 : }
555 :
556 11175 : mhas_type = (u32) gf_mpegh_escaped_value(ctx->bs, 3, 8, 8);
557 11175 : mhas_label = (u32) gf_mpegh_escaped_value(ctx->bs, 2, 8, 32);
558 11175 : mhas_size = (u32) gf_mpegh_escaped_value(ctx->bs, 11, 24, 24);
559 :
560 11175 : if (ctx->buffer_too_small)
561 : break;
562 :
563 :
564 11167 : if (mhas_type>18) {
565 0 : ctx->nb_unknown_pck++;
566 0 : if (ctx->nb_unknown_pck > ctx->pcksync) {
567 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[MHASDmx] %d packets of unknwon type, considering sync was lost\n"));
568 : consumed = 0;
569 0 : ctx->nosync = GF_TRUE;
570 0 : ctx->nb_unknown_pck = 0;
571 : break;
572 : }
573 11167 : } else if (!mhas_size) {
574 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[MHASDmx] MHAS packet with 0 payload size, considering sync was lost\n"));
575 : consumed = 0;
576 0 : ctx->nosync = GF_TRUE;
577 0 : ctx->nb_unknown_pck = 0;
578 : break;
579 : }
580 :
581 11167 : pay_start = (u32) gf_bs_get_position(ctx->bs);
582 :
583 11167 : if (ctx->buffer_too_small) break;
584 11167 : if (mhas_size > gf_bs_available(ctx->bs)) {
585 : //incomplete frame, keep in buffer
586 185 : GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[MHASDmx] incomplete packet type %d %s label "LLU" size "LLU" - keeping in buffer\n", mhas_type, mhas_pck_name(mhas_type), mhas_label, mhas_size));
587 : break;
588 : }
589 : //frame
590 10982 : if (mhas_type==2) {
591 5190 : mhas_sap = gf_bs_peek_bits(ctx->bs, 1, 0);
592 5190 : ctx->nb_unknown_pck = 0;
593 : }
594 : //config
595 5792 : else if (mhas_type==1) {
596 : s32 CICPspeakerLayoutIdx = -1;
597 : s32 numSpeakers = -1;
598 : u32 sr = 0;
599 : u32 frame_len;
600 142 : u32 pl = gf_bs_read_u8(ctx->bs);
601 142 : u32 idx = gf_bs_read_int(ctx->bs, 5);
602 142 : if (idx==0x1f)
603 0 : sr = gf_bs_read_int(ctx->bs, 24);
604 142 : else if (sr < nb_usac_sr) {
605 142 : sr = USACSampleRates[idx];
606 : }
607 142 : ctx->nb_unknown_pck = 0;
608 142 : idx = gf_bs_read_int(ctx->bs, 3);
609 142 : if ((idx==0) || (idx==2) ) frame_len = 768;
610 : else frame_len = 1024;
611 142 : gf_bs_read_int(ctx->bs, 1);
612 142 : gf_bs_read_int(ctx->bs, 1);
613 :
614 : //speaker config
615 142 : u32 speakerLayoutType = gf_bs_read_int(ctx->bs, 2);
616 142 : if (speakerLayoutType == 0) {
617 142 : CICPspeakerLayoutIdx = gf_bs_read_int(ctx->bs, 6);
618 : } else {
619 0 : numSpeakers = (s32) gf_mpegh_escaped_value(ctx->bs, 5, 8, 16) + 1;
620 : //TODO ...
621 : }
622 :
623 142 : mhas_dmx_check_pid(filter, ctx, pl, sr, frame_len, CICPspeakerLayoutIdx, numSpeakers, start + pay_start, (u32) mhas_size);
624 :
625 : has_cfg = GF_TRUE;
626 : }
627 : //audio truncation
628 5650 : else if (mhas_type==17) {
629 0 : Bool isActive = gf_bs_read_int(ctx->bs, 1);
630 0 : /*Bool ati_reserved = */gf_bs_read_int(ctx->bs, 1);
631 0 : trunc_from_begin = gf_bs_read_int(ctx->bs, 1);
632 0 : nb_trunc_samples = gf_bs_read_int(ctx->bs, 13);
633 0 : if (!isActive) {
634 : nb_trunc_samples = 0;
635 : }
636 : }
637 : //sync, syncgap
638 5650 : else if ((mhas_type==6) || (mhas_type==7)) {
639 5376 : ctx->nb_unknown_pck = 0;
640 : }
641 : #if 0
642 : //MARKER
643 : else if (mhas_type==8) {
644 : u8 marker_type = gf_bs_read_u8(ctx->bs);
645 : //config reload force
646 : if (marker_type==0x01) {}
647 : //SAP
648 : else if (marker_type==0x02) {
649 : has_marker = GF_TRUE;
650 : }
651 : }
652 : #endif
653 :
654 10982 : gf_bs_align(ctx->bs);
655 10982 : parse_end = (u32) gf_bs_get_position(ctx->bs) - pay_start;
656 : //remaining of packet payload
657 10982 : gf_bs_skip_bytes(ctx->bs, mhas_size - parse_end);
658 :
659 10982 : GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[MHASDmx] MHAS Packet type %d %s label "LLU" size "LLU"\n", mhas_type, mhas_pck_name(mhas_type), mhas_label, mhas_size));
660 :
661 10982 : if (ctx->timescale && !prev_pck_size && (cts != GF_FILTER_NO_TS) ) {
662 0 : ctx->cts = cts;
663 : cts = GF_FILTER_NO_TS;
664 : }
665 :
666 : //frame
667 10982 : if ((mhas_type==2) && ctx->opid) {
668 : GF_FilterPacket *dst;
669 5190 : u64 pck_dur = ctx->frame_len;
670 :
671 :
672 : u32 au_size;
673 5190 : if (ctx->mpha) {
674 : au_start = pay_start;
675 : au_size = mhas_size;
676 : } else {
677 2608 : au_size = (u32) gf_bs_get_position(ctx->bs) - au_start;
678 : }
679 :
680 5190 : if (nb_trunc_samples) {
681 0 : if (trunc_from_begin) {
682 0 : if (!ctx->nb_frames) {
683 0 : s64 offset = trunc_from_begin;
684 0 : if (ctx->timescale) {
685 0 : offset *= ctx->timescale;
686 0 : offset /= ctx->sample_rate;
687 : }
688 0 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DELAY , &PROP_LONGSINT( -offset));
689 : }
690 : } else {
691 0 : pck_dur -= nb_trunc_samples;
692 : }
693 : nb_trunc_samples = 0;
694 : }
695 :
696 5190 : if (ctx->timescale) {
697 0 : pck_dur *= ctx->timescale;
698 0 : pck_dur /= ctx->sample_rate;
699 : }
700 :
701 5190 : dst = gf_filter_pck_new_alloc(ctx->opid, au_size, &output);
702 5190 : if (!dst) break;
703 5190 : if (ctx->src_pck) gf_filter_pck_merge_properties(ctx->src_pck, dst);
704 :
705 5190 : memcpy(output, start + au_start, au_size);
706 5190 : if (!has_cfg)
707 : mhas_sap = 0;
708 :
709 125 : if (mhas_sap) {
710 125 : gf_filter_pck_set_sap(dst, GF_FILTER_SAP_1);
711 : }
712 5190 : gf_filter_pck_set_dts(dst, ctx->cts);
713 5190 : gf_filter_pck_set_cts(dst, ctx->cts);
714 5190 : gf_filter_pck_set_duration(dst, (u32) pck_dur);
715 5190 : if (ctx->byte_offset != GF_FILTER_NO_BO) {
716 5190 : u64 offset = (u64) (start - ctx->mhas_buffer);
717 5190 : offset += ctx->byte_offset + au_start;
718 5190 : gf_filter_pck_set_byte_offset(dst, offset);
719 : }
720 5190 : GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[MHASDmx] Send AU CTS "LLU" size %d dur %d sap %d\n", ctx->cts, au_size, (u32) pck_dur, mhas_sap));
721 5190 : gf_filter_pck_send(dst);
722 :
723 5190 : au_start += au_size;
724 : consumed = au_start;
725 5190 : ctx->nb_frames ++;
726 :
727 : mhas_dmx_update_cts(ctx);
728 : has_cfg = 0;
729 :
730 5190 : if (prev_pck_size) {
731 0 : u64 next_pos = (u64) (start + au_start - ctx->mhas_buffer);
732 : //next will be in new packet
733 0 : if (prev_pck_size <= next_pos) {
734 : prev_pck_size = 0;
735 0 : if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
736 0 : ctx->src_pck = in_pck;
737 0 : if (in_pck)
738 0 : gf_filter_pck_ref_props(&ctx->src_pck);
739 :
740 0 : if (ctx->timescale && (cts != GF_FILTER_NO_TS) ) {
741 0 : ctx->cts = cts;
742 : cts = GF_FILTER_NO_TS;
743 : }
744 : }
745 : }
746 5190 : if (remain==consumed)
747 : break;
748 :
749 5186 : if (gf_filter_pid_would_block(ctx->opid)) {
750 5161 : ctx->resume_from = 1;
751 : final_flush = GF_FALSE;
752 5161 : break;
753 : }
754 : }
755 : }
756 5358 : if (consumed) {
757 : assert(remain>=consumed);
758 5166 : remain -= consumed;
759 5166 : start += consumed;
760 : }
761 :
762 5555 : skip:
763 :
764 5363 : if (remain < ctx->mhas_buffer_size) {
765 5166 : memmove(ctx->mhas_buffer, start, remain);
766 : //update byte offset
767 5166 : if (ctx->byte_offset != GF_FILTER_NO_BO)
768 5166 : ctx->byte_offset += ctx->mhas_buffer_size - remain;
769 : }
770 5363 : ctx->mhas_buffer_size = remain;
771 5363 : if (final_flush)
772 4 : ctx->mhas_buffer_size = 0;
773 :
774 5363 : if (!ctx->mhas_buffer_size) {
775 4 : if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
776 4 : ctx->src_pck = NULL;
777 : }
778 :
779 5363 : if (in_pck)
780 197 : gf_filter_pid_drop_packet(ctx->ipid);
781 :
782 : return GF_OK;
783 : }
784 :
785 8 : static void mhas_buffer_too_small(void *udta)
786 : {
787 : GF_MHASDmxCtx *ctx = (GF_MHASDmxCtx *) udta;
788 8 : ctx->buffer_too_small = GF_TRUE;
789 8 : }
790 :
791 5 : static GF_Err mhas_dmx_initialize(GF_Filter *filter)
792 : {
793 5 : GF_MHASDmxCtx *ctx = gf_filter_get_udta(filter);
794 5 : ctx->bs = gf_bs_new((u8 *)ctx, 1, GF_BITSTREAM_READ);
795 5 : gf_bs_set_eos_callback(ctx->bs, mhas_buffer_too_small, ctx);
796 5 : return GF_OK;
797 : }
798 5 : static void mhas_dmx_finalize(GF_Filter *filter)
799 : {
800 5 : GF_MHASDmxCtx *ctx = gf_filter_get_udta(filter);
801 5 : if (ctx->bs) gf_bs_del(ctx->bs);
802 5 : if (ctx->indexes) gf_free(ctx->indexes);
803 5 : if (ctx->mhas_buffer) gf_free(ctx->mhas_buffer);
804 5 : if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
805 5 : }
806 :
807 :
808 3065 : static const char *mhas_dmx_probe_data(const u8 *data, u32 size, GF_FilterProbeScore *score)
809 : {
810 : s32 sync_pos = -1;
811 : GF_BitStream *bs;
812 : u32 nb_mhas_cfg = 0;
813 : u32 nb_mhas_frames = 0;
814 : u32 nb_mhas_unknown = 0;
815 : const u8 *ptr = data;
816 42866 : while (ptr) {
817 39801 : u32 pos = (u32) (ptr - data);
818 39801 : const u8 *sync_start = memchr(ptr, 0xC0, size - pos);
819 39801 : if (!sync_start) return NULL;
820 36741 : if ((sync_start[1]== 0x01) && (sync_start[2]==0xA5)) {
821 5 : sync_pos = pos;
822 5 : break;
823 : }
824 36736 : ptr = sync_start+1;
825 : }
826 5 : if (sync_pos<0) return NULL;
827 5 : bs = gf_bs_new(data, size, GF_BITSTREAM_READ);
828 5 : gf_bs_skip_bytes(bs, sync_pos);
829 :
830 290 : while (gf_bs_available(bs)) {
831 285 : u32 type = (u32) gf_mpegh_escaped_value(bs, 3, 8, 8);
832 285 : /*u64 label = */gf_mpegh_escaped_value(bs, 2, 8, 32);
833 285 : u64 mh_size = gf_mpegh_escaped_value(bs, 11, 24, 24);
834 285 : if (mh_size > gf_bs_available(bs))
835 : break;
836 : //MHAS config
837 280 : if (type==1) nb_mhas_cfg++;
838 275 : else if (type==2) nb_mhas_frames++;
839 145 : else if (type>18) nb_mhas_unknown++;
840 280 : gf_bs_skip_bytes(bs, mh_size);
841 : }
842 5 : gf_bs_del(bs);
843 5 : if (!nb_mhas_unknown && nb_mhas_cfg && nb_mhas_frames) {
844 5 : *score = GF_FPROBE_SUPPORTED;
845 5 : return "audio/mpegh";
846 : }
847 : return NULL;
848 : }
849 :
850 : static const GF_FilterCapability MHASDmxCaps[] =
851 : {
852 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
853 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "mhas"),
854 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "audio/mpegh"),
855 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
856 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_MHAS),
857 : CAP_BOOL(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
858 : {0},
859 : CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
860 : CAP_BOOL(GF_CAPS_INPUT,GF_PROP_PID_UNFRAMED, GF_TRUE),
861 : CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_CODECID, GF_CODECID_MHAS),
862 : CAP_BOOL(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
863 : };
864 :
865 :
866 :
867 : #define OFFS(_n) #_n, offsetof(GF_MHASDmxCtx, _n)
868 : static const GF_FilterArgs MHASDmxArgs[] =
869 : {
870 : { OFFS(index), "indexing window length", GF_PROP_DOUBLE, "1.0", NULL, 0},
871 : { OFFS(mpha), "demux MHAS and only forward audio frames", GF_PROP_BOOL, "false", NULL, 0},
872 : { OFFS(pcksync), "number of unknwon packets to tolerate before considering sync is lost", GF_PROP_UINT, "4", NULL, 0},
873 : { OFFS(nosync), "initial sync state - see filter help", GF_PROP_BOOL, "true", NULL, 0},
874 :
875 : {0}
876 : };
877 :
878 :
879 : GF_FilterRegister MHASDmxRegister = {
880 : .name = "rfmhas",
881 : GF_FS_SET_DESCRIPTION("MPEH-H Audio Stream reframer")
882 : GF_FS_SET_HELP("This filter parses MHAS files/data and outputs corresponding audio PID and frames.\n"
883 : "By default, the filter expects a MHAS stream with SYNC packets set, otherwise tune-in will fail. Using [-nosync]()=false can help parsing bitstreams with no SYNC packets.\n"
884 : "The default behavior is to dispatch a framed MHAS bitstream. To demultiplex into a raw MPEG-H Audio, use [-mpha]().\n"
885 : )
886 : .private_size = sizeof(GF_MHASDmxCtx),
887 : .args = MHASDmxArgs,
888 : .finalize = mhas_dmx_finalize,
889 : .initialize = mhas_dmx_initialize,
890 : SETCAPS(MHASDmxCaps),
891 : .configure_pid = mhas_dmx_configure_pid,
892 : .process = mhas_dmx_process,
893 : .probe_data = mhas_dmx_probe_data,
894 : .process_event = mhas_dmx_process_event
895 : };
896 :
897 :
898 2877 : const GF_FilterRegister *mhas_dmx_register(GF_FilterSession *session)
899 : {
900 2877 : return &MHASDmxRegister;
901 : }
|