1 /*
2 * GPAC - Multimedia Framework C SDK
3 *
4 * Authors: Jean Le Feuvre
5 * Copyright (c) Telecom ParisTech 2000-2017
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 #include <gpac/bitstream.h>
29 #include <gpac/internal/media_dev.h>
30
31 typedef struct
32 {
33 u64 pos;
34 Double duration;
35 } QCPIdx;
36
37 typedef struct
38 {
39 //filter args
40 Double index;
41
42 //only one input pid declared
43 GF_FilterPid *ipid;
44 //only one output pid declared
45 GF_FilterPid *opid;
46
47 u32 codecid, sample_rate, block_size;
48 Bool done;
49
50 u64 cts;
51 GF_Fraction64 duration;
52 Double start_range;
53
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 data_chunk_offset, data_chunk_size, data_chunk_remain;
61 u32 resume_from;
62 u32 remaining;
63 u32 skip_bytes;
64 u32 vrat_rate_flag, pck_size, rate_table_count;
65 QCPRateTable rate_table[8];
66
67 Bool hdr_processed;
68
69 char *buffer;
70 u32 buffer_alloc, buffer_size;
71
72 GF_BitStream *bs;
73
74
75 QCPIdx *indexes;
76 u32 index_alloc_size, index_size;
77 } GF_QCPDmxCtx;
78
79
80
81
qcpdmx_configure_pid(GF_Filter * filter,GF_FilterPid * pid,Bool is_remove)82 GF_Err qcpdmx_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
83 {
84 GF_QCPDmxCtx *ctx = gf_filter_get_udta(filter);
85
86 if (is_remove) {
87 ctx->ipid = NULL;
88 gf_filter_pid_remove(ctx->opid);
89 return GF_OK;
90 }
91 if (! gf_filter_pid_check_caps(pid))
92 return GF_NOT_SUPPORTED;
93
94 ctx->ipid = pid;
95 return GF_OK;
96 }
97
98 static GF_Err qcpdmx_process_header(GF_Filter *filter, GF_QCPDmxCtx *ctx, char *data, u32 size, GF_BitStream *file_bs);
99
qcpdmx_check_dur(GF_Filter * filter,GF_QCPDmxCtx * ctx)100 static void qcpdmx_check_dur(GF_Filter *filter, GF_QCPDmxCtx *ctx)
101 {
102 FILE *stream;
103 GF_BitStream *bs;
104 u32 i, chunk_size;
105 GF_Err e;
106 u32 data_chunk_size = 0;
107 u64 duration, cur_dur;
108 char magic[4];
109 const GF_PropertyValue *p;
110 if (!ctx->opid || ctx->timescale || ctx->file_loaded) return;
111
112 if (ctx->index<=0) {
113 ctx->file_loaded = GF_TRUE;
114 return;
115 }
116
117 p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILEPATH);
118 if (!p || !p->value.string || !strncmp(p->value.string, "gmem://", 7)) {
119 ctx->is_file = GF_FALSE;
120 ctx->file_loaded = GF_TRUE;
121 return;
122 }
123 ctx->is_file = GF_TRUE;
124
125 stream = gf_fopen(p->value.string, "rb");
126 if (!stream) return;
127
128 ctx->codecid = 0;
129 ctx->sample_rate = 8000;
130 ctx->block_size = 160;
131
132 bs = gf_bs_from_file(stream, GF_BITSTREAM_READ);
133 if (!ctx->hdr_processed ) {
134 e = qcpdmx_process_header(filter, ctx, NULL, 0, bs);
135 if (e) {
136 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[QCPDmx] Header parsed error %s\n", gf_error_to_string(e) ));
137 }
138 } else {
139 gf_bs_skip_bytes(bs, 170);
140 }
141 while (gf_bs_available(bs) ) {
142 gf_bs_read_data(bs, magic, 4);
143 chunk_size = gf_bs_read_u32_le(bs);
144
145 if (strncmp(magic, "data", 4)) {
146 gf_bs_skip_bytes(bs, chunk_size);
147 if (chunk_size%2) gf_bs_skip_bytes(bs, 1);
148 continue;
149 }
150 data_chunk_size = chunk_size;
151 break;
152 }
153 if (!data_chunk_size) {
154 gf_bs_del(bs);
155 gf_fclose(stream);
156 return;
157 }
158
159 ctx->index_size = 0;
160 ctx->data_chunk_offset = (u32) gf_ftell(stream);
161 ctx->data_chunk_size = data_chunk_size;
162
163 duration = 0;
164 cur_dur = 0;
165 while (data_chunk_size) {
166 u32 idx, size=0;
167 u64 pos;
168 pos = gf_ftell(stream);
169 /*get frame rate idx*/
170 if (ctx->vrat_rate_flag) {
171 idx = gf_fgetc(stream);
172 chunk_size-=1;
173 for (i=0; i<ctx->rate_table_count; i++) {
174 if (ctx->rate_table[i].rate_idx==idx) {
175 size = ctx->rate_table[i].pck_size;
176 break;
177 }
178 }
179 gf_fseek(stream, size, SEEK_CUR);
180 size++;
181 } else {
182 size = ctx->pck_size;
183 gf_fseek(stream, size, SEEK_CUR);
184 }
185 data_chunk_size-= size;
186
187 duration += ctx->block_size;
188 cur_dur += ctx->block_size;
189 if (cur_dur > ctx->index * ctx->sample_rate) {
190 if (!ctx->index_alloc_size) ctx->index_alloc_size = 10;
191 else if (ctx->index_alloc_size == ctx->index_size) ctx->index_alloc_size *= 2;
192 ctx->indexes = gf_realloc(ctx->indexes, sizeof(QCPIdx)*ctx->index_alloc_size);
193 ctx->indexes[ctx->index_size].pos = pos;
194 ctx->indexes[ctx->index_size].duration = (Double) duration;
195 ctx->indexes[ctx->index_size].duration /= ctx->sample_rate;
196 ctx->index_size ++;
197 cur_dur = 0;
198 }
199 }
200 gf_bs_del(bs);
201 gf_fclose(stream);
202
203 if (!ctx->duration.num || (ctx->duration.num * ctx->sample_rate != duration * ctx->duration.den)) {
204 ctx->duration.num = (s32) duration;
205 ctx->duration.den = ctx->sample_rate;
206
207 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, & PROP_FRAC64(ctx->duration));
208 }
209
210 p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILE_CACHED);
211 if (p && p->value.boolean) ctx->file_loaded = GF_TRUE;
212 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CAN_DATAREF, & PROP_BOOL(GF_TRUE ) );
213 }
214
qcpdmx_process_event(GF_Filter * filter,const GF_FilterEvent * evt)215 static Bool qcpdmx_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
216 {
217 u32 i;
218 u64 file_pos = 0;
219 GF_FilterEvent fevt;
220 GF_QCPDmxCtx *ctx = gf_filter_get_udta(filter);
221
222 switch (evt->base.type) {
223 case GF_FEVT_PLAY:
224 if (!ctx->is_playing) {
225 ctx->is_playing = GF_TRUE;
226 ctx->cts = 0;
227 ctx->remaining = 0;
228 }
229 if (! ctx->is_file) {
230 return GF_FALSE;
231 }
232 qcpdmx_check_dur(filter, ctx);
233
234 ctx->start_range = evt->play.start_range;
235 ctx->in_seek = GF_TRUE;
236 if (ctx->start_range) {
237 for (i=1; i<ctx->index_size; i++) {
238 if (ctx->indexes[i].duration>ctx->start_range) {
239 ctx->cts = (u64) (ctx->indexes[i-1].duration * ctx->sample_rate);
240 file_pos = ctx->indexes[i-1].pos;
241 break;
242 }
243 }
244 }
245 if (!ctx->initial_play_done) {
246 ctx->initial_play_done = GF_TRUE;
247 //seek will not change the current source state, don't send a seek
248 if (!file_pos) {
249 return GF_TRUE;
250 }
251 }
252 if (!file_pos) {
253 file_pos = ctx->data_chunk_offset;
254 ctx->data_chunk_remain = ctx->data_chunk_size;
255 }
256
257 //post a seek
258 GF_FEVT_INIT(fevt, GF_FEVT_SOURCE_SEEK, ctx->ipid);
259 fevt.seek.start_offset = file_pos;
260 gf_filter_pid_send_event(ctx->ipid, &fevt);
261
262 //cancel event
263 return GF_TRUE;
264
265 case GF_FEVT_STOP:
266 ctx->is_playing = GF_FALSE;
267 //don't cancel event
268 return GF_FALSE;
269
270 case GF_FEVT_SET_SPEED:
271 //cancel event
272 return GF_TRUE;
273 default:
274 break;
275 }
276 //by default don't cancel event - to rework once we have downloading in place
277 return GF_FALSE;
278 }
279
280 /*QCP codec GUIDs*/
281 static const char *QCP_QCELP_GUID_1 = "\x41\x6D\x7F\x5E\x15\xB1\xD0\x11\xBA\x91\x00\x80\x5F\xB4\xB9\x7E";
282 static const char *QCP_QCELP_GUID_2 = "\x42\x6D\x7F\x5E\x15\xB1\xD0\x11\xBA\x91\x00\x80\x5F\xB4\xB9\x7E";
283 static const char *QCP_EVRC_GUID = "\x8D\xD4\x89\xE6\x76\x90\xB5\x46\x91\xEF\x73\x6A\x51\x00\xCE\xB4";
284 static const char *QCP_SMV_GUID = "\x75\x2B\x7C\x8D\x97\xA7\x46\xED\x98\x5E\xD5\x3C\x8C\xC7\x5F\x84";
285
qcpdmx_process_header(GF_Filter * filter,GF_QCPDmxCtx * ctx,char * data,u32 size,GF_BitStream * file_bs)286 static GF_Err qcpdmx_process_header(GF_Filter *filter, GF_QCPDmxCtx *ctx, char *data, u32 size, GF_BitStream *file_bs)
287 {
288 char magic[12], GUID[17], name[81], fmt[162];
289 u32 riff_size, chunk_size, i, avg_bps;
290 Bool has_pad;
291 const GF_PropertyValue *p;
292 GF_BitStream *bs;
293
294 bs = file_bs ? file_bs : gf_bs_new(data, size, GF_BITSTREAM_READ);
295
296 gf_bs_read_data(bs, magic, 4);
297 if (strnicmp(magic, "RIFF", 4)) {
298 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[QCPDmx] Broken file: RIFF header not found\n"));
299 if (!file_bs) gf_bs_del(bs);
300 return GF_NON_COMPLIANT_BITSTREAM;
301 }
302 riff_size = gf_bs_read_u32_le(bs);
303 gf_bs_read_data(bs, fmt, 162);
304 gf_bs_seek(bs, 8);
305 gf_bs_read_data(bs, magic, 4);
306 if (strnicmp(magic, "QLCM", 4)) {
307 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[QCPDmx] Broken file: QLCM header not found\n"));
308 if (!file_bs) gf_bs_del(bs);
309 return GF_NON_COMPLIANT_BITSTREAM;
310 }
311 p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_DOWN_SIZE);
312 if (p && p->value.longuint != riff_size+8) {
313 GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[QCPDmx] Broken file: RIFF-Size %d got %d\n", p->value.uint - 8, riff_size));
314 }
315 /*fmt*/
316 gf_bs_read_data(bs, magic, 4);
317 if (strnicmp(magic, "fmt ", 4)) {
318 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[QCPDmx] Broken file: FMT not found\n"));
319 if (!file_bs) gf_bs_del(bs);
320 return GF_NON_COMPLIANT_BITSTREAM;
321 }
322 chunk_size = gf_bs_read_u32_le(bs);
323 has_pad = (chunk_size%2) ? GF_TRUE : GF_FALSE;
324 /*major = */gf_bs_read_u8(bs);
325 /*minor = */gf_bs_read_u8(bs);
326 chunk_size -= 2;
327 /*codec info*/
328 gf_bs_read_data(bs, GUID, 16);
329 GUID[16]=0;
330 /*version = */gf_bs_read_u16_le(bs);
331 chunk_size -= 18;
332 gf_bs_read_data(bs, name, 80);
333 name[80]=0;
334 chunk_size -= 80;
335 avg_bps = gf_bs_read_u16_le(bs);
336 ctx->pck_size = gf_bs_read_u16_le(bs);
337 ctx->block_size = gf_bs_read_u16_le(bs);
338 ctx->sample_rate = gf_bs_read_u16_le(bs);
339 /*bps = */gf_bs_read_u16_le(bs);
340 ctx->rate_table_count = gf_bs_read_u32_le(bs);
341 chunk_size -= 14;
342 /*skip var rate*/
343 for (i=0; i<8; i++) {
344 ctx->rate_table[i].pck_size = gf_bs_read_u8(bs);
345 ctx->rate_table[i].rate_idx = gf_bs_read_u8(bs);
346 }
347 chunk_size -= 16;
348 gf_bs_skip_bytes(bs, 5*4);/*reserved*/
349 chunk_size -= 20;
350 gf_bs_skip_bytes(bs, chunk_size);
351 if (has_pad) gf_bs_read_u8(bs);
352
353 if (!strncmp(GUID, QCP_QCELP_GUID_1, 16) || !strncmp(GUID, QCP_QCELP_GUID_2, 16)) {
354 ctx->codecid = GF_CODECID_QCELP;
355 } else if (!strncmp(GUID, QCP_EVRC_GUID, 16)) {
356 ctx->codecid = GF_CODECID_EVRC;
357 } else if (!strncmp(GUID, QCP_SMV_GUID, 16)) {
358 ctx->codecid = GF_CODECID_SMV;
359 } else {
360 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[QCPDmx] Unsupported codec GUID %s\n", GUID));
361 if (!file_bs) gf_bs_del(bs);
362 return GF_NON_COMPLIANT_BITSTREAM;
363 }
364 /*vrat*/
365 gf_bs_read_data(bs, magic, 4);
366 if (strnicmp(magic, "vrat", 4)) {
367 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[QCPDmx] Broken file: VRAT not found\n"));
368 if (!file_bs) gf_bs_del(bs);
369 return GF_NON_COMPLIANT_BITSTREAM;
370 }
371 chunk_size = gf_bs_read_u32_le(bs);
372 has_pad = (chunk_size%2) ? GF_TRUE : GF_FALSE;
373 ctx->vrat_rate_flag = gf_bs_read_u32_le(bs);
374 /*size_in_packet =*/gf_bs_read_u32_le(bs);
375 chunk_size -= 8;
376 gf_bs_skip_bytes(bs, chunk_size);
377 if (has_pad) gf_bs_read_u8(bs);
378
379 if (file_bs) return GF_OK;
380
381 gf_bs_del(bs);
382
383
384 ctx->opid = gf_filter_pid_new(filter);
385 gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
386 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, & PROP_UINT( GF_STREAM_AUDIO));
387 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_TIMESCALE, & PROP_UINT(ctx->sample_rate));
388 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLE_RATE, & PROP_UINT(ctx->sample_rate));
389 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_NUM_CHANNELS, & PROP_UINT(1) );
390 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, & PROP_UINT(ctx->codecid ) );
391 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLES_PER_FRAME, & PROP_UINT(ctx->block_size ) );
392 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_BITRATE, & PROP_UINT(avg_bps));
393 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_FRAME_SIZE, & PROP_UINT(ctx->pck_size));
394
395 qcpdmx_check_dur(filter, ctx);
396
397 return GF_OK;
398
399 }
400
qcpdmx_process(GF_Filter * filter)401 GF_Err qcpdmx_process(GF_Filter *filter)
402 {
403 GF_QCPDmxCtx *ctx = gf_filter_get_udta(filter);
404 GF_FilterPacket *pck;
405 u64 byte_offset;
406 u8 *data, *output;
407 u8 *start;
408 u32 pck_size, remain;
409 GF_Err e;
410 //update duration
411 qcpdmx_check_dur(filter, ctx);
412
413 if (ctx->done) return GF_EOS;
414
415 if (ctx->opid && !ctx->is_playing)
416 return GF_OK;
417
418 pck = gf_filter_pid_get_packet(ctx->ipid);
419 if (!pck) {
420 if (gf_filter_pid_is_eos(ctx->ipid)) {
421 if (ctx->opid)
422 gf_filter_pid_set_eos(ctx->opid);
423 assert(ctx->remaining == 0);
424 return GF_EOS;
425 }
426 return GF_OK;
427 }
428
429 data = (char *) gf_filter_pck_get_data(pck, &pck_size);
430 byte_offset = gf_filter_pck_get_byte_offset(pck);
431
432 start = data;
433 remain = pck_size;
434
435 //flush not previously dispatched data
436 if (ctx->remaining) {
437 u32 to_send = ctx->remaining;
438 if (ctx->remaining > pck_size) {
439 to_send = pck_size;
440 ctx->remaining -= pck_size;
441 } else {
442 ctx->remaining = 0;
443 }
444 if (! ctx->in_seek) {
445 GF_FilterPacket *dst_pck = gf_filter_pck_new_alloc(ctx->opid, to_send, &output);
446 memcpy(output, data, to_send);
447
448 gf_filter_pck_set_cts(dst_pck, ctx->cts);
449 gf_filter_pck_set_sap(dst_pck, GF_FILTER_SAP_1);
450 gf_filter_pck_set_framing(dst_pck, GF_FALSE, ctx->remaining ? GF_FALSE : GF_TRUE);
451 if (byte_offset != GF_FILTER_NO_BO) {
452 gf_filter_pck_set_byte_offset(dst_pck, byte_offset);
453 }
454 gf_filter_pck_send(dst_pck);
455 }
456 assert (ctx->data_chunk_remain >= to_send);
457 ctx->data_chunk_remain -= to_send;
458
459 if (ctx->remaining) {
460 gf_filter_pid_drop_packet(ctx->ipid);
461 return GF_OK;
462 }
463 ctx->cts += ctx->block_size;
464 start += to_send;
465 remain -= to_send;
466
467 if (!ctx->data_chunk_remain) {
468 ctx->done = GF_TRUE;
469 if (ctx->opid)
470 gf_filter_pid_set_eos(ctx->opid);
471 return GF_EOS;
472 }
473 }
474
475 if (ctx->resume_from) {
476 start += ctx->resume_from;
477 remain -= ctx->resume_from;
478 ctx->resume_from = 0;
479 }
480
481
482 while (remain) {
483 u32 i, chunk_size=0;
484 u32 idx = 0;
485 u32 size = 0;
486 u64 b_offset;
487 u8 *pck_data;
488 Bool has_pad;
489
490 if (!ctx->hdr_processed) {
491 if (ctx->buffer_size + remain < 170) {
492 if (ctx->buffer_alloc < ctx->buffer_size + remain) {
493 ctx->buffer_alloc = ctx->buffer_size + remain;
494 ctx->buffer = gf_realloc(ctx->buffer, ctx->buffer_alloc);
495 }
496 memcpy(ctx->buffer + ctx->buffer_size, start, remain);
497 ctx->buffer_size += remain;
498 gf_filter_pid_drop_packet(ctx->ipid);
499 return GF_OK;
500 }
501 ctx->hdr_processed = GF_TRUE;
502 if (ctx->buffer_size) {
503 e = qcpdmx_process_header(filter, ctx, ctx->buffer, ctx->buffer_size, NULL);
504 } else {
505 e = qcpdmx_process_header(filter, ctx, start, remain, NULL);
506 }
507 start += 170 - ctx->buffer_size;
508 remain -= 170 - ctx->buffer_size;
509 ctx->buffer_size = 0;
510
511 if (e) {
512 gf_filter_setup_failure(filter, e);
513 ctx->done = GF_TRUE;
514 gf_filter_pid_drop_packet(ctx->ipid);
515 return GF_EOS;
516 }
517 continue;
518 }
519 //skip current chunk
520 if (ctx->skip_bytes) {
521 if (remain<ctx->skip_bytes) {
522 ctx->skip_bytes -= remain;
523 gf_filter_pid_drop_packet(ctx->ipid);
524 return GF_OK;
525 }
526 start += ctx->skip_bytes;
527 remain -= ctx->skip_bytes;
528 ctx->skip_bytes = 0;
529 }
530
531 //load chunk tag
532 if (!ctx->data_chunk_remain) {
533 char magic[4];
534 //load chunk
535 if (remain<8) {
536 if (ctx->buffer_alloc < ctx->buffer_size + 8) {
537 ctx->buffer_alloc = ctx->buffer_size + 8;
538 ctx->buffer = gf_realloc(ctx->buffer, ctx->buffer_alloc);
539 }
540 memcpy(ctx->buffer + ctx->buffer_size, start, remain);
541 ctx->buffer_size += remain;
542 gf_filter_pid_drop_packet(ctx->ipid);
543 return GF_OK;
544 }
545 if (!ctx->buffer_size) {
546 if (!ctx->bs) {
547 ctx->bs = gf_bs_new((u8 *) start, remain, GF_BITSTREAM_READ);
548 } else {
549 gf_bs_reassign_buffer(ctx->bs, start, remain);
550 }
551 } else {
552 if (!ctx->bs) {
553 ctx->bs = gf_bs_new((u8 *) ctx->buffer, ctx->buffer_size, GF_BITSTREAM_READ);
554 } else {
555 gf_bs_reassign_buffer(ctx->bs, ctx->buffer, ctx->buffer_size);
556 }
557 }
558
559 gf_bs_read_data(ctx->bs, magic, 4);
560 chunk_size = gf_bs_read_u32_le(ctx->bs);
561 has_pad = (chunk_size%2) ? GF_TRUE : GF_FALSE;
562 start += 8-ctx->buffer_size;
563 remain -= 8-ctx->buffer_size;
564 ctx->buffer_size = 0;
565
566 //wait until we reach data chunk
567 if (strnicmp(magic, "data", 4)) {
568 ctx->skip_bytes = chunk_size;
569 if (has_pad) ctx->skip_bytes++;
570 continue;
571 } else {
572 ctx->data_chunk_size = ctx->data_chunk_remain = chunk_size;
573 }
574 }
575
576 //we are in the data chunk
577 if (!ctx->is_playing) {
578 ctx->resume_from = (u32) ( (char *)start - (char *)data);
579 return GF_OK;
580 }
581
582 b_offset = gf_filter_pck_get_byte_offset(pck);
583 if (b_offset != GF_FILTER_NO_BO) {
584 b_offset += (start - (u8 *) data);
585 }
586 /*get frame rate idx*/
587 if (ctx->vrat_rate_flag) {
588 idx = start[0];
589 //chunk_size-=1;
590 for (i=0; i<ctx->rate_table_count; i++) {
591 if (ctx->rate_table[i].rate_idx==idx) {
592 size = ctx->rate_table[i].pck_size;
593 break;
594 }
595 }
596 size++;
597 } else {
598 size = ctx->pck_size;
599 }
600
601 if (size > remain) {
602 ctx->remaining = size - remain;
603 size = remain;
604 } else {
605 ctx->remaining = 0;
606 }
607
608 if (ctx->in_seek) {
609 u64 nb_samples_at_seek = (u64) (ctx->start_range * ctx->sample_rate);
610 if (ctx->cts + ctx->block_size >= nb_samples_at_seek) {
611 //u32 samples_to_discard = (ctx->cts + ctx->block_size ) - nb_samples_at_seek;
612 ctx->in_seek = GF_FALSE;
613 }
614 }
615
616 if (!ctx->in_seek) {
617 GF_FilterPacket *dst_pck = gf_filter_pck_new_alloc(ctx->opid, size, &pck_data);
618 memcpy(pck_data, start, size);
619
620 gf_filter_pck_set_framing(dst_pck, GF_TRUE, ctx->remaining ? GF_FALSE : GF_TRUE);
621 gf_filter_pck_set_cts(dst_pck, ctx->cts);
622 gf_filter_pck_set_sap(dst_pck, GF_FILTER_SAP_1);
623 gf_filter_pck_set_duration(dst_pck, ctx->block_size);
624 if (b_offset != GF_FILTER_NO_BO)
625 gf_filter_pck_set_byte_offset(dst_pck, b_offset);
626
627 gf_filter_pck_send(dst_pck);
628 }
629
630 assert (ctx->data_chunk_remain >= size);
631 ctx->data_chunk_remain -= size;
632 if (!ctx->data_chunk_remain) {
633 ctx->done = GF_TRUE;
634 if (ctx->opid)
635 gf_filter_pid_set_eos(ctx->opid);
636 break;
637 }
638
639 if (ctx->remaining) break;
640 ctx->cts += ctx->block_size;
641 start += size;
642 remain -= size;
643
644
645 //don't demux too much of input, abort when we would block. This avoid dispatching
646 //a huge number of frames in a single call
647 if (gf_filter_pid_would_block(ctx->opid)) {
648 ctx->resume_from = (u32) ((char *)start - (char *)data);
649 return GF_OK;
650 }
651 }
652 gf_filter_pid_drop_packet(ctx->ipid);
653
654 return GF_OK;
655 }
656
qcpdmx_finalize(GF_Filter * filter)657 static void qcpdmx_finalize(GF_Filter *filter)
658 {
659 GF_QCPDmxCtx *ctx = gf_filter_get_udta(filter);
660 if (ctx->indexes) gf_free(ctx->indexes);
661 if (ctx->bs) gf_bs_del(ctx->bs);
662 if (ctx->buffer) gf_free(ctx->buffer);
663 }
664
qcpdmx_probe_data(const u8 * data,u32 size,GF_FilterProbeScore * score)665 static const char *qcpdmx_probe_data(const u8 *data, u32 size, GF_FilterProbeScore *score)
666 {
667 char magic[5];
668 Bool is_qcp = GF_TRUE;
669 GF_BitStream *bs = gf_bs_new(data, size, GF_BITSTREAM_READ);
670
671 magic[4] = 0;
672 gf_bs_read_data(bs, magic, 4);
673 if (strnicmp(magic, "RIFF", 4)) {
674 is_qcp = GF_FALSE;
675 } else {
676 /*riff_size = */gf_bs_read_u32_le(bs);
677 gf_bs_read_data(bs, magic, 4);
678 if (strnicmp(magic, "QLCM", 4)) {
679 is_qcp = GF_FALSE;
680 }
681 }
682 gf_bs_del(bs);
683 if (!is_qcp) return NULL;
684 *score = GF_FPROBE_SUPPORTED;
685 return "audio/qcp";
686 }
687
688 static const GF_FilterCapability QCPDmxCaps[] =
689 {
690 CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
691 CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "qcp"),
692 CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "audio/qcp"),
693 CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
694 CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_QCELP),
695 CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_SMV),
696 CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_EVRC),
697 CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_EVRC_PV),
698 CAP_BOOL(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
699 };
700
701
702 #define OFFS(_n) #_n, offsetof(GF_QCPDmxCtx, _n)
703 static const GF_FilterArgs QCPDmxArgs[] =
704 {
705 { OFFS(index), "indexing window length", GF_PROP_DOUBLE, "1.0", NULL, 0},
706 {0}
707 };
708
709
710 GF_FilterRegister QCPDmxRegister = {
711 .name = "rfqcp",
712 GF_FS_SET_DESCRIPTION("QCP reframer")
713 GF_FS_SET_HELP("This filter parses QCP files/data and outputs corresponding audio PID and frames.")
714 .private_size = sizeof(GF_QCPDmxCtx),
715 .args = QCPDmxArgs,
716 .finalize = qcpdmx_finalize,
717 SETCAPS(QCPDmxCaps),
718 .configure_pid = qcpdmx_configure_pid,
719 .process = qcpdmx_process,
720 .probe_data = qcpdmx_probe_data,
721 .process_event = qcpdmx_process_event
722 };
723
724
qcpdmx_register(GF_FilterSession * session)725 const GF_FilterRegister *qcpdmx_register(GF_FilterSession *session)
726 {
727 return &QCPDmxRegister;
728 }
729
730