Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2000-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / MP3 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 : #ifndef GPAC_DISABLE_AV_PARSERS
31 :
32 : typedef struct
33 : {
34 : u64 pos;
35 : Double duration;
36 : } MP3Idx;
37 :
38 : typedef struct
39 : {
40 : //filter args
41 : Double index;
42 : Bool expart;
43 :
44 : //only one input pid declared
45 : GF_FilterPid *ipid;
46 : //only one output pid declared
47 : GF_FilterPid *opid;
48 :
49 : GF_BitStream *bs;
50 : u64 file_pos, cts, prev_cts;
51 : u32 sr, nb_ch, codecid;
52 : GF_Fraction64 duration;
53 : Double start_range;
54 : Bool in_seek;
55 : u32 timescale;
56 : Bool is_playing;
57 : Bool is_file;
58 : Bool initial_play_done, file_loaded;
59 :
60 : u32 hdr;
61 :
62 : u8 *mp3_buffer;
63 : u32 mp3_buffer_size, mp3_buffer_alloc, resume_from;
64 : u64 byte_offset;
65 :
66 : GF_FilterPacket *src_pck;
67 :
68 : Bool recompute_cts;
69 : MP3Idx *indexes;
70 : u32 index_alloc_size, index_size;
71 :
72 : u32 tag_size;
73 : u8 *id3_buffer;
74 : u32 id3_buffer_size, id3_buffer_alloc;
75 :
76 : GF_FilterPid *vpid;
77 : } GF_MP3DmxCtx;
78 :
79 :
80 :
81 :
82 72 : GF_Err mp3_dmx_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
83 : {
84 : const GF_PropertyValue *p;
85 72 : GF_MP3DmxCtx *ctx = gf_filter_get_udta(filter);
86 :
87 72 : if (is_remove) {
88 0 : ctx->ipid = NULL;
89 0 : if (ctx->opid) {
90 0 : gf_filter_pid_remove(ctx->opid);
91 0 : ctx->opid = NULL;
92 : }
93 : return GF_OK;
94 : }
95 72 : if (! gf_filter_pid_check_caps(pid))
96 : return GF_NOT_SUPPORTED;
97 :
98 72 : ctx->ipid = pid;
99 72 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_TIMESCALE);
100 72 : if (p) ctx->timescale = p->value.uint;
101 :
102 72 : p = gf_filter_pid_get_property_str(pid, "nocts");
103 72 : if (p && p->value.boolean) ctx->recompute_cts = GF_TRUE;
104 69 : else ctx->recompute_cts = GF_FALSE;
105 :
106 72 : if (ctx->timescale && !ctx->opid) {
107 19 : ctx->opid = gf_filter_pid_new(filter);
108 19 : gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
109 19 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_UNFRAMED, NULL);
110 : }
111 : return GF_OK;
112 : }
113 :
114 13960 : static void mp3_dmx_check_dur(GF_Filter *filter, GF_MP3DmxCtx *ctx)
115 : {
116 : FILE *stream;
117 : u64 duration, cur_dur;
118 : s32 prev_sr = -1;
119 : const GF_PropertyValue *p;
120 13960 : if (!ctx->opid || ctx->timescale || ctx->file_loaded) return;
121 :
122 51 : if (ctx->index<=0) {
123 25 : ctx->file_loaded = GF_TRUE;
124 : return;
125 : }
126 :
127 26 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILEPATH);
128 26 : if (!p || !p->value.string || !strncmp(p->value.string, "gmem://", 7)) {
129 1 : ctx->is_file = GF_FALSE;
130 1 : ctx->file_loaded = GF_TRUE;
131 : return;
132 : }
133 25 : ctx->is_file = GF_TRUE;
134 :
135 25 : stream = gf_fopen(p->value.string, "rb");
136 25 : if (!stream) return;
137 :
138 25 : ctx->index_size = 0;
139 :
140 : duration = 0;
141 : cur_dur = 0;
142 : while (1) {
143 : u32 sr, dur;
144 : u64 pos;
145 9150 : u32 hdr = gf_mp3_get_next_header(stream);
146 9150 : if (!hdr) break;
147 9125 : sr = gf_mp3_sampling_rate(hdr);
148 :
149 9125 : if ((prev_sr>=0) && (prev_sr != sr)) {
150 1 : duration *= sr;
151 1 : duration /= prev_sr;
152 :
153 1 : cur_dur *= sr;
154 1 : cur_dur /= prev_sr;
155 : }
156 9125 : prev_sr = sr;
157 9125 : dur = gf_mp3_window_size(hdr);
158 9125 : duration += dur;
159 9125 : cur_dur += dur;
160 9125 : pos = gf_ftell(stream);
161 9125 : if (cur_dur > ctx->index * prev_sr) {
162 213 : if (!ctx->index_alloc_size) ctx->index_alloc_size = 10;
163 189 : else if (ctx->index_alloc_size == ctx->index_size) ctx->index_alloc_size *= 2;
164 213 : ctx->indexes = gf_realloc(ctx->indexes, sizeof(MP3Idx)*ctx->index_alloc_size);
165 213 : ctx->indexes[ctx->index_size].pos = pos - 4;
166 213 : ctx->indexes[ctx->index_size].duration = (Double) duration;
167 213 : ctx->indexes[ctx->index_size].duration /= prev_sr;
168 213 : ctx->index_size ++;
169 : cur_dur = 0;
170 : }
171 :
172 9125 : pos = gf_ftell(stream);
173 9125 : gf_fseek(stream, pos + gf_mp3_frame_size(hdr) - 4, SEEK_SET);
174 : }
175 25 : gf_fclose(stream);
176 :
177 25 : if (!ctx->duration.num || (ctx->duration.num * prev_sr != duration * ctx->duration.den)) {
178 25 : ctx->duration.num = (s32) duration;
179 25 : ctx->duration.den = prev_sr ;
180 :
181 25 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, & PROP_FRAC64(ctx->duration));
182 : }
183 :
184 25 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILE_CACHED);
185 25 : if (p && p->value.boolean) ctx->file_loaded = GF_TRUE;
186 25 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CAN_DATAREF, & PROP_BOOL(GF_TRUE ) );
187 : }
188 :
189 :
190 : #include <gpac/utf.h>
191 4 : static void id3dmx_set_string(GF_FilterPid *apid, char *name, u8 *buf, Bool is_dyn)
192 : {
193 4 : if ((buf[0]==0xFF) || (buf[0]==0xFE)) {
194 3 : const u16 *sptr = (u16 *) (buf+2);
195 3 : s32 len = (s32) ( UTF8_MAX_BYTES_PER_CHAR * gf_utf8_wcslen(sptr) );
196 3 : char *tmp = gf_malloc(len+1);
197 3 : len = (s32) gf_utf8_wcstombs(tmp, len, &sptr);
198 3 : if (len>=0) {
199 3 : tmp[len] = 0;
200 3 : if (is_dyn) {
201 0 : gf_filter_pid_set_property_dyn(apid, name, &PROP_STRING(tmp) );
202 : } else {
203 3 : gf_filter_pid_set_property_str(apid, name, &PROP_STRING(tmp) );
204 : }
205 : } else {
206 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[MP3Dmx] Corrupted ID3 text frame %s\n", name));
207 : }
208 3 : gf_free(tmp);
209 : } else {
210 1 : if (is_dyn) {
211 0 : gf_filter_pid_set_property_dyn(apid, name, &PROP_STRING(buf) );
212 : } else {
213 1 : gf_filter_pid_set_property_str(apid, name, &PROP_STRING(buf) );
214 : }
215 : }
216 4 : }
217 :
218 1 : void id3dmx_flush(GF_Filter *filter, u8 *id3_buf, u32 id3_buf_size, GF_FilterPid *audio_pid, GF_FilterPid **video_pid_p)
219 : {
220 1 : GF_BitStream *bs = gf_bs_new(id3_buf, id3_buf_size, GF_BITSTREAM_READ);
221 : char *sep_desc;
222 : char *_buf=NULL;
223 : u32 buf_alloc=0;
224 1 : gf_bs_skip_bytes(bs, 3);
225 1 : /*u8 major = */gf_bs_read_u8(bs);
226 1 : /*u8 minor = */gf_bs_read_u8(bs);
227 1 : /*u8 unsync = */gf_bs_read_int(bs, 1);
228 1 : u8 ext_hdr = gf_bs_read_int(bs, 1);
229 1 : gf_bs_read_int(bs, 6);
230 1 : u32 size = gf_id3_read_size(bs);
231 :
232 :
233 : if (ext_hdr) {
234 :
235 : }
236 :
237 1 : while (size && (gf_bs_available(bs)>=10) ) {
238 : char *buf;
239 : char szTag[1024];
240 : char *sep;
241 : s32 tag_idx;
242 : u32 pic_size;
243 : //u32 pic_type;
244 5 : u32 ftag = gf_bs_read_u32(bs);
245 5 : u32 fsize = gf_id3_read_size(bs);
246 5 : /*u16 fflags = */gf_bs_read_u16(bs);
247 :
248 5 : size -= 10;
249 5 : if (!fsize)
250 : break;
251 :
252 4 : if (size<fsize) {
253 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[MP3Dmx] Broken ID3 frame tag %s, size %d but remaining bytes %d\n", gf_4cc_to_str(ftag), fsize, size));
254 : break;
255 : }
256 :
257 4 : if (buf_alloc<=fsize) {
258 2 : _buf = gf_realloc(_buf, fsize+3);
259 : buf_alloc = fsize+3;
260 : }
261 : //read into _buf+1 so that buf+1 is always %2 mem aligned as it can be loaded as unsigned short
262 4 : gf_bs_read_data(bs, _buf+1, fsize);
263 4 : _buf[fsize+1]=0;
264 4 : _buf[fsize+2]=0;
265 : buf = _buf+1;
266 :
267 4 : tag_idx = gf_itags_find_by_id3tag(ftag);
268 4 : if (tag_idx>=0) {
269 4 : const char *tag_name = gf_itags_get_name((u32) tag_idx);
270 4 : id3dmx_set_string(audio_pid, (char *) tag_name, buf+1, GF_FALSE);
271 0 : } else if (ftag==GF_ID3V2_FRAME_TXXX) {
272 0 : sep = memchr(buf, 0, fsize);
273 0 : if (sep) {
274 0 : if (!stricmp(buf+1, "comment")) {
275 0 : id3dmx_set_string(audio_pid, "comment", sep+1, GF_FALSE);
276 : } else {
277 : strcpy(szTag, "tag_");
278 : strncat(szTag, buf+1, 1019);
279 0 : id3dmx_set_string(audio_pid, szTag, sep+1, GF_TRUE);
280 : }
281 : }
282 0 : } else if (ftag == GF_ID3V2_FRAME_APIC) {
283 : //first char is text encoding
284 : //then mime
285 0 : sep = memchr(buf+1, 0, fsize-1);
286 : /*pic_type = sep[1];*/
287 0 : sep_desc = memchr(sep+2, 0, fsize-1);
288 :
289 0 : if (sep_desc) {
290 : GF_Err e;
291 0 : pic_size = (u32) ( (sep_desc + 1) - buf);
292 0 : pic_size = fsize - pic_size;
293 :
294 0 : if (video_pid_p) {
295 0 : e = gf_filter_pid_raw_new(filter, NULL, NULL, buf+1, NULL, sep_desc+1, pic_size, GF_FALSE, video_pid_p);
296 0 : if (e) {
297 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[MP3Dmx] error setting up video pid for cover art: %s\n", gf_error_to_string(e) ));
298 : }
299 0 : if (*video_pid_p) {
300 : u8 *out_buffer;
301 : GF_FilterPacket *dst_pck;
302 0 : gf_filter_pid_set_name(*video_pid_p, "CoverArt");
303 0 : gf_filter_pid_set_property(*video_pid_p, GF_PROP_PID_COVER_ART, &PROP_BOOL(GF_TRUE));
304 0 : dst_pck = gf_filter_pck_new_alloc(*video_pid_p, pic_size, &out_buffer);
305 0 : if (dst_pck) {
306 0 : gf_filter_pck_set_framing(dst_pck, GF_TRUE, GF_TRUE);
307 0 : memcpy(out_buffer, sep_desc+1, pic_size);
308 0 : gf_filter_pck_send(dst_pck);
309 : }
310 :
311 0 : gf_filter_pid_set_eos(*video_pid_p);
312 : }
313 : } else {
314 0 : gf_filter_pid_set_property(audio_pid, GF_PROP_PID_COVER_ART, &PROP_DATA(sep_desc+1, pic_size) );
315 : }
316 : }
317 : } else {
318 0 : sprintf(szTag, "tag_%s", gf_4cc_to_str(ftag));
319 0 : if ((ftag>>24) == 'T') {
320 0 : id3dmx_set_string(audio_pid, szTag, buf+1, GF_TRUE);
321 : } else {
322 0 : gf_filter_pid_set_property_dyn(audio_pid, szTag, &PROP_DATA(buf, fsize) );
323 : }
324 : break;
325 : }
326 4 : size -= fsize;
327 : }
328 1 : gf_bs_del(bs);
329 1 : if (_buf) gf_free(_buf);
330 1 : }
331 1 : static void mp3_dmx_flush_id3(GF_Filter *filter, GF_MP3DmxCtx *ctx)
332 : {
333 1 : id3dmx_flush(filter, ctx->id3_buffer, ctx->id3_buffer_size, ctx->opid, ctx->expart ? &ctx->vpid : NULL);
334 1 : ctx->id3_buffer_size = 0;
335 1 : }
336 :
337 23361 : static void mp3_dmx_check_pid(GF_Filter *filter, GF_MP3DmxCtx *ctx)
338 : {
339 : u32 sr;
340 :
341 23361 : if (!ctx->opid) {
342 51 : ctx->opid = gf_filter_pid_new(filter);
343 51 : mp3_dmx_check_dur(filter, ctx);
344 : }
345 :
346 23361 : if ((ctx->sr == gf_mp3_sampling_rate(ctx->hdr)) && (ctx->nb_ch == gf_mp3_num_channels(ctx->hdr) )
347 23291 : && (ctx->codecid == gf_mp3_object_type_indication(ctx->hdr) )
348 : )
349 : return;
350 :
351 : //copy properties at init or reconfig
352 70 : gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
353 70 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, & PROP_UINT( GF_STREAM_AUDIO));
354 70 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_UNFRAMED, NULL );
355 70 : if (ctx->is_file && ctx->index) {
356 25 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PLAYBACK_MODE, & PROP_UINT(GF_PLAYBACK_MODE_FASTFORWARD) );
357 : }
358 70 : if (ctx->duration.num)
359 25 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, & PROP_FRAC64(ctx->duration));
360 :
361 70 : if (!ctx->timescale) gf_filter_pid_set_name(ctx->opid, "audio");
362 :
363 70 : ctx->nb_ch = gf_mp3_num_channels(ctx->hdr);
364 70 : ctx->codecid = gf_mp3_object_type_indication(ctx->hdr);
365 70 : sr = gf_mp3_sampling_rate(ctx->hdr);
366 :
367 70 : if (!ctx->timescale) {
368 : //we change sample rate, change cts
369 51 : if (ctx->cts && ctx->sr && (ctx->sr != sr)) {
370 0 : ctx->cts *= sr;
371 0 : ctx->cts /= ctx->sr;
372 : }
373 : }
374 70 : ctx->sr = sr;
375 :
376 70 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_TIMESCALE, & PROP_UINT(ctx->timescale ? ctx->timescale : sr));
377 70 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLE_RATE, & PROP_UINT(sr));
378 70 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_NUM_CHANNELS, & PROP_UINT(ctx->nb_ch) );
379 70 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, & PROP_UINT(gf_mp3_object_type_indication(ctx->hdr) ) );
380 70 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLES_PER_FRAME, & PROP_UINT(gf_mp3_window_size(ctx->hdr) ) );
381 :
382 70 : if (!gf_sys_is_test_mode() ) {
383 0 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_BITRATE, & PROP_UINT(gf_mp3_bit_rate(ctx->hdr) ) );
384 : }
385 :
386 70 : if (ctx->id3_buffer_size)
387 1 : mp3_dmx_flush_id3(filter, ctx);
388 :
389 : }
390 :
391 1542 : static Bool mp3_dmx_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
392 : {
393 : u32 i;
394 : GF_FilterEvent fevt;
395 1542 : GF_MP3DmxCtx *ctx = gf_filter_get_udta(filter);
396 :
397 1542 : if (evt->base.on_pid != ctx->opid) return GF_TRUE;
398 :
399 112 : switch (evt->base.type) {
400 77 : case GF_FEVT_PLAY:
401 77 : if (!ctx->is_playing) {
402 77 : ctx->is_playing = GF_TRUE;
403 : }
404 77 : if (! ctx->is_file) {
405 45 : if (evt->play.start_range || ctx->initial_play_done) {
406 0 : ctx->mp3_buffer_size = 0;
407 0 : ctx->resume_from = 0;
408 : }
409 45 : ctx->initial_play_done = GF_TRUE;
410 45 : return GF_FALSE;
411 : }
412 32 : mp3_dmx_check_dur(filter, ctx);
413 :
414 32 : ctx->start_range = evt->play.start_range;
415 32 : ctx->in_seek = GF_TRUE;
416 32 : ctx->file_pos = 0;
417 32 : if (ctx->start_range) {
418 1 : for (i=1; i<ctx->index_size; i++) {
419 2 : if (ctx->indexes[i].duration>ctx->start_range) {
420 1 : ctx->cts = (u64) (ctx->indexes[i-1].duration * ctx->sr);
421 1 : ctx->file_pos = ctx->indexes[i-1].pos;
422 1 : break;
423 : }
424 : }
425 : }
426 32 : if (!ctx->initial_play_done) {
427 25 : ctx->initial_play_done = GF_TRUE;
428 : //seek will not change the current source state, don't send a seek
429 25 : if (!ctx->file_pos)
430 : return GF_TRUE;
431 : }
432 7 : ctx->mp3_buffer_size = 0;
433 7 : ctx->resume_from = 0;
434 : //post a seek
435 7 : GF_FEVT_INIT(fevt, GF_FEVT_SOURCE_SEEK, ctx->ipid);
436 7 : fevt.seek.start_offset = ctx->file_pos;
437 7 : gf_filter_pid_send_event(ctx->ipid, &fevt);
438 :
439 : //cancel event
440 7 : return GF_TRUE;
441 :
442 33 : case GF_FEVT_STOP:
443 33 : ctx->is_playing = GF_FALSE;
444 33 : if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
445 33 : ctx->src_pck = NULL;
446 : //don't cancel event
447 33 : return GF_FALSE;
448 :
449 : case GF_FEVT_SET_SPEED:
450 : //cancel event
451 : return GF_TRUE;
452 : default:
453 : break;
454 : }
455 : //by default don't cancel event - to rework once we have downloading in place
456 0 : return GF_FALSE;
457 : }
458 :
459 23310 : static GFINLINE void mp3_dmx_update_cts(GF_MP3DmxCtx *ctx)
460 : {
461 23310 : u32 nb_samp = gf_mp3_window_size(ctx->hdr);
462 23310 : if (ctx->timescale) {
463 9161 : u64 inc = nb_samp;
464 9161 : inc *= ctx->timescale;
465 9161 : inc /= ctx->sr;
466 9161 : ctx->cts += inc;
467 : } else {
468 14149 : ctx->cts += nb_samp;
469 : }
470 23310 : }
471 :
472 14388 : GF_Err mp3_dmx_process(GF_Filter *filter)
473 : {
474 14388 : GF_MP3DmxCtx *ctx = gf_filter_get_udta(filter);
475 : GF_FilterPacket *pck, *dst_pck;
476 : u8 *data, *output;
477 : u8 *start;
478 : u32 pck_size, remain, prev_pck_size;
479 : u64 cts = GF_FILTER_NO_TS;
480 :
481 : //always reparse duration
482 14388 : if (!ctx->duration.num)
483 13877 : mp3_dmx_check_dur(filter, ctx);
484 :
485 14388 : if (ctx->opid && !ctx->is_playing)
486 : return GF_OK;
487 :
488 14245 : pck = gf_filter_pid_get_packet(ctx->ipid);
489 14245 : if (!pck) {
490 9479 : if (gf_filter_pid_is_eos(ctx->ipid)) {
491 107 : if (!ctx->mp3_buffer_size) {
492 57 : if (ctx->opid)
493 57 : gf_filter_pid_set_eos(ctx->opid);
494 57 : if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
495 57 : ctx->src_pck = NULL;
496 57 : return GF_EOS;
497 : }
498 : } else {
499 : return GF_OK;
500 : }
501 : }
502 :
503 4816 : prev_pck_size = ctx->mp3_buffer_size;
504 4816 : if (pck && !ctx->resume_from) {
505 4715 : data = (char *) gf_filter_pck_get_data(pck, &pck_size);
506 :
507 4715 : if (ctx->byte_offset != GF_FILTER_NO_BO) {
508 572 : u64 byte_offset = gf_filter_pck_get_byte_offset(pck);
509 572 : if (!ctx->mp3_buffer_size) {
510 75 : ctx->byte_offset = byte_offset;
511 497 : } else if (ctx->byte_offset + ctx->mp3_buffer_size != byte_offset) {
512 1 : ctx->byte_offset = GF_FILTER_NO_BO;
513 1 : if ((byte_offset != GF_FILTER_NO_BO) && (byte_offset>ctx->mp3_buffer_size) ) {
514 1 : ctx->byte_offset = byte_offset - ctx->mp3_buffer_size;
515 : }
516 : }
517 : }
518 :
519 4715 : if (ctx->mp3_buffer_size + pck_size > ctx->mp3_buffer_alloc) {
520 352 : ctx->mp3_buffer_alloc = ctx->mp3_buffer_size + pck_size;
521 352 : ctx->mp3_buffer = gf_realloc(ctx->mp3_buffer, ctx->mp3_buffer_alloc);
522 : }
523 4715 : memcpy(ctx->mp3_buffer + ctx->mp3_buffer_size, data, pck_size);
524 4715 : ctx->mp3_buffer_size += pck_size;
525 : }
526 :
527 : //input pid sets some timescale - we flushed pending data , update cts
528 4816 : if (ctx->timescale && pck) {
529 4162 : cts = gf_filter_pck_get_cts(pck);
530 : }
531 :
532 4162 : if (cts == GF_FILTER_NO_TS) {
533 : //avoids updating cts
534 : prev_pck_size = 0;
535 : }
536 :
537 4816 : remain = ctx->mp3_buffer_size;
538 4816 : start = ctx->mp3_buffer;
539 :
540 4816 : if (ctx->resume_from) {
541 51 : start += ctx->resume_from - 1;
542 51 : remain -= ctx->resume_from - 1;
543 51 : ctx->resume_from = 0;
544 : }
545 :
546 28129 : while (remain) {
547 : u8 *sync;
548 : Bool skip_id3v1=GF_FALSE;
549 28079 : u32 bytes_skipped=0, size, nb_samp, bytes_to_drop=0;;
550 :
551 28079 : if (!ctx->tag_size && (remain>3)) {
552 :
553 : /* Did we read an ID3v2 ? */
554 28069 : if (start[0] == 'I' && start[1] == 'D' && start[2] == '3') {
555 1 : if (remain<10)
556 51 : return GF_OK;
557 :
558 1 : ctx->tag_size = ((start[9] & 0x7f) + ((start[8] & 0x7f) << 7) + ((start[7] & 0x7f) << 14) + ((start[6] & 0x7f) << 21));
559 :
560 : bytes_to_drop = 10;
561 1 : if (ctx->id3_buffer_alloc < ctx->tag_size+10) {
562 1 : ctx->id3_buffer = gf_realloc(ctx->id3_buffer, ctx->tag_size+10);
563 1 : ctx->id3_buffer_alloc = ctx->tag_size+10;
564 : }
565 1 : memcpy(ctx->id3_buffer, start, 10);
566 1 : ctx->id3_buffer_size = 10;
567 : goto drop_byte;
568 : }
569 : }
570 28078 : if (ctx->tag_size) {
571 1 : if (ctx->tag_size>remain) {
572 : bytes_to_drop = remain;
573 0 : ctx->tag_size-=remain;
574 : } else {
575 : bytes_to_drop = ctx->tag_size;
576 1 : ctx->tag_size = 0;
577 : }
578 1 : memcpy(ctx->id3_buffer + ctx->id3_buffer_size, start, bytes_to_drop);
579 1 : ctx->id3_buffer_size += bytes_to_drop;
580 :
581 1 : if (!ctx->tag_size && ctx->opid) {
582 0 : mp3_dmx_flush_id3(filter, ctx);
583 : }
584 : goto drop_byte;
585 :
586 : }
587 :
588 28077 : ctx->hdr = gf_mp3_get_next_header_mem(start, remain, &bytes_skipped);
589 :
590 : //couldn't find sync byte in this packet
591 28077 : if (!ctx->hdr) {
592 : break;
593 : }
594 28068 : sync = start + bytes_skipped;
595 :
596 28068 : size = gf_mp3_frame_size(ctx->hdr);
597 :
598 :
599 : //ready to send packet
600 28068 : if (size + 1 < remain-bytes_skipped) {
601 : //make sure we are sync!
602 23312 : if (sync[size] !=0xFF) {
603 1 : if ((sync[size]=='T') && (sync[size+1]=='A') && (sync[size+2]=='G')) {
604 : skip_id3v1=GF_TRUE;
605 : } else {
606 1 : GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[MP3Dmx] invalid frame, resyncing\n"));
607 : goto drop_byte;
608 : }
609 : }
610 : }
611 : //otherwise wait for next frame, unless if end of stream
612 4756 : else if (pck) {
613 : break;
614 : }
615 : //ready to send packet
616 23361 : mp3_dmx_check_pid(filter, ctx);
617 :
618 23361 : if (!ctx->is_playing) {
619 51 : ctx->resume_from = (u32) (sync - ctx->mp3_buffer + 1);
620 51 : return GF_OK;
621 : }
622 :
623 23310 : nb_samp = gf_mp3_window_size(ctx->hdr);
624 :
625 23310 : if (ctx->in_seek) {
626 61 : u64 nb_samples_at_seek = (u64) (ctx->start_range * ctx->sr);
627 61 : if (ctx->cts + nb_samp >= nb_samples_at_seek) {
628 : //u32 samples_to_discard = (ctx->cts + nb_samp ) - nb_samples_at_seek;
629 30 : ctx->in_seek = GF_FALSE;
630 : }
631 : }
632 :
633 23310 : bytes_to_drop = bytes_skipped + size;
634 23310 : if (ctx->timescale && !prev_pck_size && (cts != GF_FILTER_NO_TS) ) {
635 1038 : ctx->cts = cts;
636 : cts = GF_FILTER_NO_TS;
637 : }
638 :
639 23310 : if (!ctx->in_seek) {
640 23279 : dst_pck = gf_filter_pck_new_alloc(ctx->opid, size, &output);
641 23279 : if (!dst_pck) break;
642 23279 : memcpy(output, sync, size);
643 :
644 23279 : gf_filter_pck_set_cts(dst_pck, ctx->cts);
645 23279 : gf_filter_pck_set_duration(dst_pck, nb_samp);
646 23279 : gf_filter_pck_set_sap(dst_pck, GF_FILTER_SAP_1);
647 23279 : gf_filter_pck_set_framing(dst_pck, GF_TRUE, GF_TRUE);
648 :
649 23279 : if (ctx->byte_offset != GF_FILTER_NO_BO) {
650 14118 : gf_filter_pck_set_byte_offset(dst_pck, ctx->byte_offset + bytes_skipped);
651 : }
652 :
653 23279 : gf_filter_pck_send(dst_pck);
654 : }
655 23310 : mp3_dmx_update_cts(ctx);
656 :
657 : //TODO, parse id3v1 ??
658 23310 : if (skip_id3v1)
659 0 : bytes_to_drop+=128;
660 :
661 : //truncated last frame
662 23310 : if (bytes_to_drop>remain) {
663 16 : GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[MP3Dmx] truncated frame!\n"));
664 : bytes_to_drop=remain;
665 : }
666 :
667 46605 : drop_byte:
668 23311 : if (!bytes_to_drop) {
669 : bytes_to_drop = 1;
670 : }
671 23313 : start += bytes_to_drop;
672 23313 : remain -= bytes_to_drop;
673 :
674 23313 : if (prev_pck_size) {
675 4143 : if (prev_pck_size > bytes_to_drop) prev_pck_size -= bytes_to_drop;
676 : else {
677 : prev_pck_size=0;
678 4143 : if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
679 4143 : ctx->src_pck = pck;
680 4143 : if (pck)
681 4143 : gf_filter_pck_ref_props(&ctx->src_pck);
682 : }
683 : }
684 23313 : if (ctx->byte_offset != GF_FILTER_NO_BO)
685 14152 : ctx->byte_offset += bytes_to_drop;
686 : }
687 :
688 4765 : if (!pck) {
689 50 : ctx->mp3_buffer_size = 0;
690 50 : return mp3_dmx_process(filter);
691 : } else {
692 4715 : if (remain) {
693 4715 : memmove(ctx->mp3_buffer, start, remain);
694 : }
695 4715 : ctx->mp3_buffer_size = remain;
696 4715 : gf_filter_pid_drop_packet(ctx->ipid);
697 : }
698 4715 : return GF_OK;
699 : }
700 :
701 70 : static void mp3_dmx_finalize(GF_Filter *filter)
702 : {
703 70 : GF_MP3DmxCtx *ctx = gf_filter_get_udta(filter);
704 70 : if (ctx->bs) gf_bs_del(ctx->bs);
705 70 : if (ctx->indexes) gf_free(ctx->indexes);
706 70 : if (ctx->mp3_buffer) gf_free(ctx->mp3_buffer);
707 70 : if (ctx->id3_buffer) gf_free(ctx->id3_buffer);
708 70 : if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
709 70 : }
710 :
711 :
712 3074 : static const char *mp3_dmx_probe_data(const u8 *data, u32 size, GF_FilterProbeScore *score)
713 : {
714 : u32 nb_frames=0;
715 3074 : u32 pos=0;
716 : u32 prev_pos=0;
717 : s32 prev_sr_idx=-1;
718 : s32 prev_ch=-1;
719 : s32 prev_layer=-1;
720 : s32 init_pos = -1;
721 : Bool has_id3 = GF_FALSE;
722 :
723 : /* Check for ID3 */
724 3074 : if (size>= 10) {
725 3070 : if (data[0] == 'I' && data[1] == 'D' && data[2] == '3') {
726 1 : u32 tag_size = ((data[9] & 0x7f) + ((data[8] & 0x7f) << 7) + ((data[7] & 0x7f) << 14) + ((data[6] & 0x7f) << 21));
727 :
728 1 : if (tag_size+10>size) {
729 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("ID3 tag detected size %d but probe data only %d bytes, will rely on file extension (try increasing probe size using --block_size)\n", tag_size+10, size));
730 0 : *score = GF_FPROBE_EXT_MATCH;
731 0 : return "mp3|mp2|mp1";
732 : }
733 1 : data += tag_size+10;
734 1 : size -= tag_size+10;
735 : has_id3 = GF_TRUE;
736 : }
737 : }
738 :
739 1413 : while (1) {
740 4487 : u32 hdr = gf_mp3_get_next_header_mem(data, size, &pos);
741 4487 : if (!hdr) break;
742 :
743 2299 : if (init_pos<0) init_pos = pos;
744 :
745 2299 : if (gf_mp3_version(hdr) > 3)
746 : break;
747 : //check sample rate
748 2299 : u8 val = (hdr >> 10) & 0x3;
749 2299 : if (val>2)
750 : break;
751 2299 : u32 fsize = gf_mp3_frame_size(hdr);
752 2299 : if (prev_pos && pos) {
753 : nb_frames=0;
754 : break;
755 : }
756 :
757 1485 : if (prev_sr_idx>=0) {
758 252 : if ((u8) prev_sr_idx != val) {
759 : nb_frames=0;
760 : break;
761 : }
762 : }
763 1485 : prev_sr_idx = val;
764 :
765 1485 : val = gf_mp3_num_channels(hdr);
766 1485 : if (prev_ch>=0) {
767 252 : if ((u8) prev_ch != val) {
768 : nb_frames=0;
769 : break;
770 : }
771 : }
772 1485 : prev_ch = val;
773 :
774 1485 : val = gf_mp3_layer(hdr);
775 1485 : if (prev_layer>=0) {
776 252 : if ((u8) prev_layer != val) {
777 : nb_frames=0;
778 : break;
779 : }
780 : }
781 1485 : prev_layer = val;
782 :
783 1485 : if (fsize + pos > size) {
784 11 : nb_frames++;
785 11 : break;
786 : }
787 :
788 : prev_pos = pos;
789 1474 : nb_frames++;
790 1474 : if (nb_frames>4) break;
791 : if (size < fsize + pos) break;
792 1413 : size -= fsize + pos;
793 1413 : data += fsize + pos;
794 : }
795 :
796 3074 : if (nb_frames>=2) {
797 63 : *score = (init_pos==0) ? GF_FPROBE_SUPPORTED : GF_FPROBE_MAYBE_SUPPORTED;
798 63 : return "audio/mp3";
799 : }
800 3011 : if (nb_frames && has_id3) {
801 0 : *score = GF_FPROBE_MAYBE_SUPPORTED;
802 0 : return "audio/mp3";
803 : }
804 : return NULL;
805 : }
806 :
807 : static const GF_FilterCapability MP3DmxCaps[] =
808 : {
809 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
810 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "mp3|mp2|mp1"),
811 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "audio/mp3|audio/x-mp3"),
812 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
813 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_MPEG_AUDIO),
814 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_MPEG2_PART3),
815 : CAP_BOOL(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
816 : {0},
817 : CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
818 : CAP_BOOL(GF_CAPS_INPUT,GF_PROP_PID_UNFRAMED, GF_TRUE),
819 : CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_CODECID, GF_CODECID_MPEG_AUDIO),
820 : CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_CODECID, GF_CODECID_MPEG2_PART3),
821 : CAP_BOOL(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
822 : {0},
823 : //also declare generic file output for embedded files (cover art & co), but explicit to skip this cap in chain resolution
824 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
825 : CAP_UINT(GF_CAPS_OUTPUT | GF_CAPFLAG_LOADED_FILTER ,GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE)
826 : };
827 :
828 :
829 :
830 : #define OFFS(_n) #_n, offsetof(GF_MP3DmxCtx, _n)
831 : static const GF_FilterArgs MP3DmxArgs[] =
832 : {
833 : { OFFS(index), "indexing window length", GF_PROP_DOUBLE, "1.0", NULL, 0},
834 : { OFFS(expart), "expose pictures as a dedicated video pid", GF_PROP_BOOL, "false", NULL, 0},
835 : {0}
836 : };
837 :
838 :
839 : GF_FilterRegister MP3DmxRegister = {
840 : .name = "rfmp3",
841 : GF_FS_SET_DESCRIPTION("MP3 reframer")
842 : GF_FS_SET_HELP("This filter parses MPEG-1/2 audio files/data and outputs corresponding audio PID and frames.")
843 : .private_size = sizeof(GF_MP3DmxCtx),
844 : .args = MP3DmxArgs,
845 : .finalize = mp3_dmx_finalize,
846 : SETCAPS(MP3DmxCaps),
847 : .configure_pid = mp3_dmx_configure_pid,
848 : .process = mp3_dmx_process,
849 : .probe_data = mp3_dmx_probe_data,
850 : .process_event = mp3_dmx_process_event
851 : };
852 :
853 :
854 2877 : const GF_FilterRegister *mp3_dmx_register(GF_FilterSession *session)
855 : {
856 2877 : return &MP3DmxRegister;
857 : }
858 :
859 : #else
860 : const GF_FilterRegister *mp3_dmx_register(GF_FilterSession *session)
861 : {
862 : return NULL;
863 : }
864 : #endif // GPAC_DISABLE_AV_PARSERS
865 :
|