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 / AMR&EVRC&SMV 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/filters.h>
27 : #include <gpac/constants.h>
28 :
29 : typedef struct
30 : {
31 : u64 pos;
32 : Double duration;
33 : } AMRIdx;
34 :
35 : typedef struct
36 : {
37 : //filter args
38 : Double index;
39 :
40 : //only one input pid declared
41 : GF_FilterPid *ipid;
42 : //only one output pid declared
43 : GF_FilterPid *opid;
44 :
45 : u32 start_offset;
46 : u32 codecid, sample_rate, block_size;
47 :
48 : u32 bitrate;
49 : u64 file_pos, cts;
50 :
51 : u16 amr_mode_set;
52 :
53 : GF_Fraction64 duration;
54 : Double start_range;
55 : Bool in_seek;
56 : u32 timescale;
57 : Bool is_playing;
58 : Bool is_file;
59 : Bool initial_play_done, file_loaded;
60 : Bool skip_magic;
61 :
62 : u32 hdr;
63 : u32 resume_from;
64 : u32 remaining;
65 :
66 : AMRIdx *indexes;
67 : u32 index_alloc_size, index_size;
68 : } GF_AMRDmxCtx;
69 :
70 :
71 :
72 :
73 9 : GF_Err amrdmx_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
74 : {
75 : const GF_PropertyValue *p;
76 9 : GF_AMRDmxCtx *ctx = gf_filter_get_udta(filter);
77 :
78 9 : if (is_remove) {
79 0 : ctx->ipid = NULL;
80 0 : if (ctx->opid) {
81 0 : gf_filter_pid_remove(ctx->opid);
82 0 : ctx->opid = NULL;
83 : }
84 : return GF_OK;
85 : }
86 9 : if (! gf_filter_pid_check_caps(pid))
87 : return GF_NOT_SUPPORTED;
88 :
89 9 : ctx->ipid = pid;
90 9 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_TIMESCALE);
91 9 : if (p) ctx->timescale = p->value.uint;
92 :
93 9 : ctx->start_offset = 6;
94 9 : ctx->sample_rate = 8000;
95 9 : ctx->block_size = 160;
96 :
97 9 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_CODECID);
98 9 : if (p) {
99 0 : if (ctx->codecid && (ctx->codecid != p->value.uint)) {
100 : return GF_NOT_SUPPORTED;
101 : }
102 0 : ctx->codecid = p->value.uint;
103 0 : if (ctx->codecid == GF_CODECID_AMR_WB) {
104 0 : ctx->sample_rate = 16000;
105 0 : ctx->block_size = 320;
106 : }
107 0 : ctx->skip_magic = GF_FALSE;
108 0 : if (!ctx->opid) {
109 0 : ctx->opid = gf_filter_pid_new(filter);
110 0 : gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
111 0 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_UNFRAMED, NULL);
112 : }
113 : }
114 : return GF_OK;
115 : }
116 :
117 42890 : static void amrdmx_check_dur(GF_Filter *filter, GF_AMRDmxCtx *ctx)
118 : {
119 : FILE *stream;
120 : u32 i;
121 : u64 duration, cur_dur, rate;
122 : char magic[20];
123 : const GF_PropertyValue *p;
124 85778 : if (!ctx->opid || ctx->timescale || ctx->file_loaded) return;
125 :
126 9 : if (ctx->index<=0) {
127 7 : ctx->file_loaded = GF_TRUE;
128 : return;
129 : }
130 :
131 2 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILEPATH);
132 2 : if (!p || !p->value.string || !strncmp(p->value.string, "gmem://", 7)) {
133 0 : ctx->is_file = GF_FALSE;
134 0 : ctx->file_loaded = GF_TRUE;
135 : return;
136 : }
137 2 : ctx->is_file = GF_TRUE;
138 :
139 2 : stream = gf_fopen(p->value.string, "rb");
140 2 : if (!stream) return;
141 :
142 2 : ctx->codecid = GF_CODECID_NONE;
143 2 : ctx->start_offset = 6;
144 2 : ctx->sample_rate = 8000;
145 2 : ctx->block_size = 160;
146 2 : i = (u32) gf_fread(magic, 20, stream);
147 2 : if (i != 20) return;
148 :
149 2 : if (!strnicmp(magic, "#!AMR\n", 6)) {
150 1 : gf_fseek(stream, 6, SEEK_SET);
151 1 : ctx->codecid = GF_CODECID_AMR;
152 : }
153 1 : else if (!strnicmp(magic, "#!EVRC\n", 7)) {
154 0 : gf_fseek(stream, 7, SEEK_SET);
155 0 : ctx->start_offset = 7;
156 0 : ctx->codecid = GF_CODECID_EVRC;
157 : }
158 1 : else if (!strnicmp(magic, "#!SMV\n", 6)) {
159 0 : gf_fseek(stream, 6, SEEK_SET);
160 0 : ctx->codecid = GF_CODECID_SMV;
161 : }
162 1 : else if (!strnicmp(magic, "#!AMR-WB\n", 9)) {
163 1 : ctx->codecid = GF_CODECID_AMR_WB;
164 1 : ctx->start_offset = 9;
165 1 : ctx->sample_rate = 16000;
166 1 : ctx->block_size = 320;
167 1 : gf_fseek(stream, 9, SEEK_SET);
168 : }
169 : else if (!strnicmp(magic, "#!AMR_MC1.0\n", 12)) return;
170 : else if (!strnicmp(magic, "#!AMR-WB_MC1.0\n", 15)) return;
171 : else return;
172 :
173 2 : ctx->index_size = 0;
174 :
175 : cur_dur = 0;
176 : duration = 0;
177 21370 : while (!gf_feof(stream)) {
178 : u32 size=0;
179 : u64 pos;
180 : u8 toc, ft;
181 21368 : toc = gf_fgetc(stream);
182 :
183 21368 : switch (ctx->codecid) {
184 1498 : case GF_CODECID_AMR:
185 1498 : ft = (toc >> 3) & 0x0F;
186 1498 : size = (u32)GF_AMR_FRAME_SIZE[ft];
187 : break;
188 19870 : case GF_CODECID_AMR_WB:
189 19870 : ft = (toc >> 3) & 0x0F;
190 19870 : size = (u32)GF_AMR_WB_FRAME_SIZE[ft];
191 : break;
192 : default:
193 0 : for (i=0; i<GF_SMV_EVRC_RATE_TO_SIZE_NB; i++) {
194 0 : if (GF_SMV_EVRC_RATE_TO_SIZE[2*i]==toc) {
195 : /*remove rate_type byte*/
196 0 : size = (u32)GF_SMV_EVRC_RATE_TO_SIZE[2*i+1] - 1;
197 : break;
198 : }
199 : }
200 : break;
201 : }
202 21368 : duration += ctx->block_size;
203 21368 : cur_dur += ctx->block_size;
204 21368 : pos = gf_ftell(stream);
205 21368 : if (cur_dur > ctx->index * ctx->sample_rate) {
206 418 : if (!ctx->index_alloc_size) ctx->index_alloc_size = 10;
207 416 : else if (ctx->index_alloc_size == ctx->index_size) ctx->index_alloc_size *= 2;
208 418 : ctx->indexes = gf_realloc(ctx->indexes, sizeof(AMRIdx)*ctx->index_alloc_size);
209 418 : ctx->indexes[ctx->index_size].pos = pos - 1;
210 418 : ctx->indexes[ctx->index_size].duration = (Double) duration;
211 418 : ctx->indexes[ctx->index_size].duration /= ctx->sample_rate;
212 418 : ctx->index_size ++;
213 : cur_dur = 0;
214 : }
215 21368 : if (size) gf_fseek(stream, size, SEEK_CUR);
216 : }
217 2 : rate = gf_ftell(stream);
218 2 : gf_fclose(stream);
219 :
220 2 : if (!ctx->duration.num || (ctx->duration.num * ctx->sample_rate != duration * ctx->duration.den)) {
221 2 : ctx->duration.num = (u32) duration;
222 2 : ctx->duration.den = ctx->sample_rate;
223 :
224 2 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, & PROP_FRAC64(ctx->duration));
225 :
226 2 : if (duration && !gf_sys_is_test_mode() ) {
227 0 : rate *= 8 * ctx->duration.den;
228 0 : rate /= ctx->duration.num;
229 0 : ctx->bitrate = (u32) rate;
230 : }
231 : }
232 :
233 2 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILE_CACHED);
234 2 : if (p && p->value.boolean) ctx->file_loaded = GF_TRUE;
235 2 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CAN_DATAREF, & PROP_BOOL(GF_TRUE ) );
236 : }
237 :
238 43376 : static void amrdmx_check_pid(GF_Filter *filter, GF_AMRDmxCtx *ctx, u16 amr_mode_set)
239 : {
240 43376 : if (ctx->opid) {
241 43367 : if (ctx->amr_mode_set != amr_mode_set) {
242 9 : ctx->amr_mode_set = amr_mode_set;
243 9 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_AMR_MODE_SET, & PROP_UINT( amr_mode_set));
244 : }
245 : return;
246 : }
247 :
248 9 : ctx->opid = gf_filter_pid_new(filter);
249 9 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, & PROP_UINT( GF_STREAM_AUDIO));
250 :
251 9 : amrdmx_check_dur(filter, ctx);
252 :
253 9 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_TIMESCALE, & PROP_UINT(ctx->sample_rate));
254 9 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLE_RATE, & PROP_UINT(ctx->sample_rate));
255 9 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_NUM_CHANNELS, & PROP_UINT(1) );
256 9 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, & PROP_UINT(ctx->codecid ) );
257 9 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLES_PER_FRAME, & PROP_UINT(ctx->block_size ) );
258 9 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_AMR_MODE_SET, & PROP_UINT(ctx->amr_mode_set));
259 :
260 9 : if (ctx->bitrate) {
261 0 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_BITRATE, & PROP_UINT(ctx->bitrate));
262 : }
263 :
264 9 : if (ctx->is_file && ctx->index) {
265 2 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PLAYBACK_MODE, & PROP_UINT(GF_PLAYBACK_MODE_FASTFORWARD) );
266 : }
267 : }
268 :
269 42867 : static Bool amrdmx_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
270 : {
271 : u32 i;
272 : GF_FilterEvent fevt;
273 42867 : GF_AMRDmxCtx *ctx = gf_filter_get_udta(filter);
274 :
275 42867 : switch (evt->base.type) {
276 9 : case GF_FEVT_PLAY:
277 9 : if (!ctx->is_playing) {
278 9 : ctx->is_playing = GF_TRUE;
279 9 : ctx->cts = 0;
280 9 : ctx->remaining = 0;
281 : }
282 9 : if (! ctx->is_file) {
283 : return GF_FALSE;
284 : }
285 2 : amrdmx_check_dur(filter, ctx);
286 :
287 2 : ctx->start_range = evt->play.start_range;
288 2 : ctx->in_seek = GF_TRUE;
289 2 : ctx->file_pos = 0;
290 2 : if (ctx->start_range) {
291 0 : for (i=1; i<ctx->index_size; i++) {
292 0 : if (ctx->indexes[i].duration>ctx->start_range) {
293 0 : ctx->cts = (u64) (ctx->indexes[i-1].duration * ctx->sample_rate);
294 0 : ctx->file_pos = ctx->indexes[i-1].pos;
295 0 : break;
296 : }
297 : }
298 : }
299 2 : if (!ctx->initial_play_done) {
300 2 : ctx->initial_play_done = GF_TRUE;
301 : //seek will not change the current source state, don't send a seek
302 2 : if (!ctx->file_pos) {
303 2 : ctx->skip_magic = GF_TRUE;
304 2 : return GF_TRUE;
305 : }
306 : }
307 : //post a seek
308 0 : GF_FEVT_INIT(fevt, GF_FEVT_SOURCE_SEEK, ctx->ipid);
309 0 : if (!ctx->file_pos)
310 0 : ctx->skip_magic = GF_TRUE;
311 0 : fevt.seek.start_offset = ctx->file_pos;
312 0 : gf_filter_pid_send_event(ctx->ipid, &fevt);
313 :
314 : //cancel event
315 0 : return GF_TRUE;
316 :
317 5 : case GF_FEVT_STOP:
318 5 : ctx->is_playing = GF_FALSE;
319 : //don't cancel event
320 5 : return GF_FALSE;
321 :
322 : case GF_FEVT_SET_SPEED:
323 : //cancel event
324 : return GF_TRUE;
325 : default:
326 : break;
327 : }
328 : //by default don't cancel event - to rework once we have downloading in place
329 42853 : return GF_FALSE;
330 : }
331 :
332 : static GFINLINE void amrdmx_update_cts(GF_AMRDmxCtx *ctx)
333 : {
334 43362 : if (ctx->timescale) {
335 0 : u64 inc = ctx->block_size;
336 0 : inc *= ctx->timescale;
337 0 : inc /= ctx->sample_rate;
338 0 : ctx->cts += inc;
339 : } else {
340 43362 : ctx->cts += ctx->block_size;
341 : }
342 : }
343 :
344 42879 : GF_Err amrdmx_process(GF_Filter *filter)
345 : {
346 42879 : GF_AMRDmxCtx *ctx = gf_filter_get_udta(filter);
347 : GF_FilterPacket *pck, *dst_pck;
348 : u64 byte_offset;
349 : u8 *data, *output;
350 : u8 *start;
351 : u32 pck_size, remain;
352 :
353 : //update duration
354 42879 : amrdmx_check_dur(filter, ctx);
355 :
356 42879 : if (ctx->opid && !ctx->is_playing)
357 : return GF_OK;
358 :
359 42857 : pck = gf_filter_pid_get_packet(ctx->ipid);
360 42857 : if (!pck) {
361 4 : if (gf_filter_pid_is_eos(ctx->ipid)) {
362 4 : if (ctx->opid)
363 4 : gf_filter_pid_set_eos(ctx->opid);
364 : assert(ctx->remaining == 0);
365 : return GF_EOS;
366 : }
367 : return GF_OK;
368 : }
369 :
370 42853 : data = (char *) gf_filter_pck_get_data(pck, &pck_size);
371 42853 : byte_offset = gf_filter_pck_get_byte_offset(pck);
372 :
373 : start = data;
374 42853 : remain = pck_size;
375 :
376 : //flush not previously dispatched data
377 42853 : if (ctx->remaining) {
378 : u32 to_send = ctx->remaining;
379 494 : if (ctx->remaining > pck_size) {
380 : to_send = pck_size;
381 0 : ctx->remaining -= pck_size;
382 : } else {
383 494 : ctx->remaining = 0;
384 : }
385 494 : if (! ctx->in_seek) {
386 494 : dst_pck = gf_filter_pck_new_alloc(ctx->opid, to_send, &output);
387 494 : if (!dst_pck) return GF_OUT_OF_MEM;
388 494 : memcpy(output, data, to_send);
389 :
390 494 : gf_filter_pck_set_cts(dst_pck, ctx->cts);
391 494 : gf_filter_pck_set_sap(dst_pck, GF_FILTER_SAP_1);
392 494 : gf_filter_pck_set_framing(dst_pck, GF_FALSE, ctx->remaining ? GF_FALSE : GF_TRUE);
393 494 : if (byte_offset != GF_FILTER_NO_BO) {
394 494 : gf_filter_pck_set_byte_offset(dst_pck, byte_offset);
395 : }
396 494 : gf_filter_pck_send(dst_pck);
397 : }
398 :
399 494 : if (ctx->remaining) {
400 0 : gf_filter_pid_drop_packet(ctx->ipid);
401 0 : return GF_OK;
402 : }
403 : amrdmx_update_cts(ctx);
404 494 : start += to_send;
405 494 : remain -= to_send;
406 : }
407 :
408 : //input pid sets some timescale - we flushed pending data , update cts
409 42853 : if (ctx->timescale) {
410 0 : u64 cts = gf_filter_pck_get_cts(pck);
411 0 : if (cts != GF_FILTER_NO_TS)
412 0 : ctx->cts = cts;
413 : }
414 42853 : if (ctx->skip_magic) {
415 :
416 18 : if (!strnicmp(start, "#!AMR\n", 6)) {
417 10 : ctx->start_offset = 6;
418 10 : ctx->codecid = GF_CODECID_AMR;
419 : }
420 8 : else if (!strnicmp(start, "#!EVRC\n", 7)) {
421 0 : ctx->start_offset = 7;
422 0 : ctx->codecid = GF_CODECID_EVRC;
423 : }
424 8 : else if (!strnicmp(start, "#!SMV\n", 6)) {
425 0 : ctx->start_offset = 6;
426 0 : ctx->codecid = GF_CODECID_SMV;
427 : }
428 8 : else if (!strnicmp(start, "#!AMR-WB\n", 9)) {
429 8 : ctx->codecid = GF_CODECID_AMR_WB;
430 8 : ctx->start_offset = 9;
431 8 : ctx->sample_rate = 16000;
432 8 : ctx->block_size = 320;
433 : }
434 18 : start += ctx->start_offset;
435 18 : remain -= ctx->start_offset;
436 : }
437 42853 : if (ctx->resume_from) {
438 42333 : start += ctx->resume_from;
439 42333 : remain -= ctx->resume_from;
440 42333 : ctx->resume_from = 0;
441 : }
442 :
443 :
444 43388 : while (remain) {
445 : u8 toc, ft;
446 43376 : u16 amr_mode_set = ctx->amr_mode_set;
447 : u32 size=0, i;
448 :
449 43376 : toc = start[0];
450 43376 : if (!toc) {
451 0 : GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[AMRDmx] Could not find TOC word in packet, dropping\n"));
452 : break;
453 : }
454 43376 : switch (ctx->codecid) {
455 3470 : case GF_CODECID_AMR:
456 3470 : ft = (toc >> 3) & 0x0F;
457 :
458 : /*update mode set (same mechanism for both AMR and AMR-WB*/
459 3470 : amr_mode_set |= (1<<ft);
460 3470 : size = (u32)GF_AMR_FRAME_SIZE[ft];
461 3470 : break;
462 39906 : case GF_CODECID_AMR_WB:
463 39906 : ft = (toc >> 3) & 0x0F;
464 39906 : size = (u32)GF_AMR_WB_FRAME_SIZE[ft];
465 :
466 : /*update mode set (same mechanism for both AMR and AMR-WB*/
467 39906 : amr_mode_set |= (1<<ft);
468 39906 : break;
469 : case GF_CODECID_NONE:
470 : size=0;
471 : break;
472 : default:
473 0 : for (i=0; i<GF_SMV_EVRC_RATE_TO_SIZE_NB; i++) {
474 0 : if (GF_SMV_EVRC_RATE_TO_SIZE[2*i]==toc) {
475 : /*remove rate_type byte*/
476 0 : size = (u32)GF_SMV_EVRC_RATE_TO_SIZE[2*i+1] - 1;
477 0 : break;
478 : }
479 : }
480 : break;
481 : }
482 :
483 43376 : if (!size) {
484 0 : GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[AMRDmx] Broken TOC, trying resync\n"));
485 0 : start++;
486 0 : remain--;
487 0 : continue;
488 : }
489 : //ready to send packet
490 43376 : amrdmx_check_pid(filter, ctx, amr_mode_set);
491 :
492 43376 : if (!ctx->is_playing) return GF_OK;
493 43367 : size++;
494 43367 : if (size > remain) {
495 499 : ctx->remaining = size - remain;
496 : size = remain;
497 : }
498 :
499 43367 : if (ctx->in_seek) {
500 2 : u64 nb_samples_at_seek = (u64) (ctx->start_range * ctx->sample_rate);
501 2 : if (ctx->cts + ctx->block_size >= nb_samples_at_seek) {
502 : //u32 samples_to_discard = (ctx->cts + ctx->block_size ) - nb_samples_at_seek;
503 2 : ctx->in_seek = GF_FALSE;
504 : }
505 : }
506 43367 : if (!ctx->in_seek) {
507 43367 : dst_pck = gf_filter_pck_new_alloc(ctx->opid, size, &output);
508 43367 : if (!dst_pck) return GF_OUT_OF_MEM;
509 :
510 43367 : memcpy(output, start, size);
511 :
512 43367 : gf_filter_pck_set_cts(dst_pck, ctx->cts);
513 43367 : gf_filter_pck_set_sap(dst_pck, GF_FILTER_SAP_1);
514 43367 : gf_filter_pck_set_duration(dst_pck, ctx->block_size);
515 43367 : gf_filter_pck_set_framing(dst_pck, GF_TRUE, ctx->remaining ? GF_FALSE : GF_TRUE);
516 :
517 43367 : if (byte_offset != GF_FILTER_NO_BO) {
518 : u64 boffset = byte_offset;
519 43367 : boffset += start - data;
520 43367 : gf_filter_pck_set_byte_offset(dst_pck, boffset);
521 : }
522 :
523 43367 : gf_filter_pck_send(dst_pck);
524 : }
525 43367 : start += size;
526 43367 : remain -= size;
527 :
528 43367 : ctx->skip_magic = 0;
529 43367 : if (ctx->remaining) break;
530 : amrdmx_update_cts(ctx);
531 :
532 : //don't demux too much of input, abort when we would block. This avoid dispatching
533 : //a huge number of frames in a single call
534 42868 : if (gf_filter_pid_would_block(ctx->opid)) {
535 42333 : ctx->resume_from = (u32) ( (char *)start - (char *)data);
536 42333 : return GF_OK;
537 : }
538 : }
539 511 : gf_filter_pid_drop_packet(ctx->ipid);
540 :
541 511 : return GF_OK;
542 : }
543 :
544 9 : static GF_Err amrdmx_initialize(GF_Filter *filter)
545 : {
546 9 : GF_AMRDmxCtx *ctx = gf_filter_get_udta(filter);
547 9 : ctx->skip_magic = GF_TRUE;
548 9 : return GF_OK;
549 : }
550 :
551 9 : static void amrdmx_finalize(GF_Filter *filter)
552 : {
553 9 : GF_AMRDmxCtx *ctx = gf_filter_get_udta(filter);
554 9 : if (ctx->indexes) gf_free(ctx->indexes);
555 9 : }
556 :
557 3074 : static const char * amrdmx_probe_data(const u8 *data, u32 size, GF_FilterProbeScore *score)
558 : {
559 3074 : if (!strnicmp(data, "#!AMR\n", 6)) {
560 5 : *score = GF_FPROBE_SUPPORTED;
561 5 : return "audio/amr";
562 : }
563 3069 : else if (!strnicmp(data, "#!AMR-WB\n", 9)) {
564 4 : *score = GF_FPROBE_SUPPORTED;
565 4 : return "audio/amr";
566 : }
567 3065 : else if (!strnicmp(data, "#!EVRC\n", 7)) {
568 0 : *score = GF_FPROBE_SUPPORTED;
569 0 : return "audio/evrc";
570 : }
571 3065 : else if (!strnicmp(data, "#!SMV\n", 6)) {
572 0 : *score = GF_FPROBE_SUPPORTED;
573 0 : return "audio/smv";
574 : }
575 : return NULL;
576 : }
577 :
578 : static const GF_FilterCapability AMRDmxCaps[] =
579 : {
580 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
581 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "amr|awb|evc|smv"),
582 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "audio/amr|audio/evrc|audio/smv"),
583 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
584 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_AMR),
585 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_AMR_WB),
586 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_SMV),
587 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_EVRC),
588 : CAP_BOOL(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
589 : };
590 :
591 : #define OFFS(_n) #_n, offsetof(GF_AMRDmxCtx, _n)
592 : static const GF_FilterArgs AMRDmxArgs[] =
593 : {
594 : { OFFS(index), "indexing window length", GF_PROP_DOUBLE, "1.0", NULL, 0},
595 : {0}
596 : };
597 :
598 :
599 : GF_FilterRegister AMRDmxRegister = {
600 : .name = "rfamr",
601 : GF_FS_SET_DESCRIPTION("AMR/EVRC reframer")
602 : GF_FS_SET_HELP("This filter parses AMR, AMR Wideband, EVRC and SMV files/data and outputs corresponding audio PID and frames.")
603 : .private_size = sizeof(GF_AMRDmxCtx),
604 : .args = AMRDmxArgs,
605 : .initialize = amrdmx_initialize,
606 : .finalize = amrdmx_finalize,
607 : SETCAPS(AMRDmxCaps),
608 : .configure_pid = amrdmx_configure_pid,
609 : .process = amrdmx_process,
610 : .probe_data = amrdmx_probe_data,
611 : .process_event = amrdmx_process_event
612 : };
613 :
614 :
615 2877 : const GF_FilterRegister *amrdmx_register(GF_FilterSession *session)
616 : {
617 2877 : return &AMRDmxRegister;
618 : }
619 :
|