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 / QCP stream to file 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 :
30 : #include <gpac/internal/media_dev.h>
31 :
32 :
33 : typedef struct
34 : {
35 : //opts
36 : Bool exporter, mpeg2;
37 :
38 : //only one input pid declared
39 : GF_FilterPid *ipid;
40 : //only one output pid declared
41 : GF_FilterPid *opid;
42 :
43 : u32 codecid;
44 : Bool first;
45 :
46 : GF_Fraction64 duration;
47 :
48 : char GUID[16];
49 : u32 qcp_type, needs_rate_byte;
50 : QCPRateTable rtable[8];
51 : unsigned int *qcp_rates, rt_cnt; /*contains constants*/
52 : Bool has_qcp_pad;
53 : Bool needs_final_pach;
54 : u32 data_size;
55 : u32 nb_frames;
56 :
57 : } GF_QCPMxCtx;
58 :
59 :
60 : /*QCP codec GUIDs*/
61 : static const char *QCP_QCELP_GUID_1 = "\x41\x6D\x7F\x5E\x15\xB1\xD0\x11\xBA\x91\x00\x80\x5F\xB4\xB9\x7E";
62 : static const char *QCP_EVRC_GUID = "\x8D\xD4\x89\xE6\x76\x90\xB5\x46\x91\xEF\x73\x6A\x51\x00\xCE\xB4";
63 : static const char *QCP_SMV_GUID = "\x75\x2B\x7C\x8D\x97\xA7\x46\xED\x98\x5E\xD5\x3C\x8C\xC7\x5F\x84";
64 :
65 1 : GF_Err qcpmx_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
66 : {
67 : u32 sr, chan, bps;
68 : const GF_PropertyValue *p;
69 1 : GF_QCPMxCtx *ctx = gf_filter_get_udta(filter);
70 :
71 1 : if (is_remove) {
72 0 : ctx->ipid = NULL;
73 0 : if (ctx->opid) {
74 0 : gf_filter_pid_remove(ctx->opid);
75 0 : ctx->opid = NULL;
76 : }
77 : return GF_OK;
78 : }
79 1 : if (! gf_filter_pid_check_caps(pid))
80 : return GF_NOT_SUPPORTED;
81 :
82 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_CODECID);
83 1 : if (!p) return GF_NOT_SUPPORTED;
84 1 : ctx->codecid = p->value.uint;
85 :
86 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAMPLE_RATE);
87 1 : if (!p) return GF_NOT_SUPPORTED;
88 1 : sr = p->value.uint;
89 :
90 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_NUM_CHANNELS);
91 1 : if (!p) return GF_NOT_SUPPORTED;
92 1 : chan = p->value.uint;
93 :
94 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_FORMAT);
95 1 : bps = p ? gf_audio_fmt_bit_depth(p->value.uint) : 16;
96 :
97 :
98 1 : if (!ctx->opid) {
99 1 : ctx->opid = gf_filter_pid_new(filter);
100 1 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE) );
101 : }
102 :
103 1 : switch (ctx->codecid) {
104 1 : case GF_CODECID_QCELP:
105 1 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_FILE_EXT, &PROP_STRING("qcp") );
106 1 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_MIME, &PROP_STRING("audio/qcp") );
107 1 : ctx->qcp_type = 1;
108 1 : memcpy(ctx->GUID, QCP_QCELP_GUID_1, sizeof(char)*16);
109 : break;
110 0 : case GF_CODECID_EVRC_PV:
111 : case GF_CODECID_EVRC:
112 0 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_FILE_EXT, &PROP_STRING("evc") );
113 0 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_MIME, &PROP_STRING("audio/qcp") );
114 0 : memcpy(ctx->GUID, QCP_EVRC_GUID, sizeof(char)*16);
115 0 : ctx->qcp_type = 3;
116 0 : break;
117 0 : case GF_CODECID_SMV:
118 0 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_FILE_EXT, &PROP_STRING("smv") );
119 0 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_MIME, &PROP_STRING("audio/qcp") );
120 0 : ctx->qcp_type = 2;
121 0 : memcpy(ctx->GUID, QCP_SMV_GUID, sizeof(char)*16);
122 : break;
123 : }
124 :
125 1 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_FILE_EXT, &PROP_STRING("qcp") );
126 1 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_MIME, &PROP_STRING("audio/qcp") );
127 1 : ctx->first = GF_TRUE;
128 :
129 1 : if (ctx->exporter) {
130 0 : GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("Exporting %s - SampleRate %d %d channels %d bits per sample\n", gf_codecid_name(ctx->codecid), sr, chan, bps));
131 : }
132 :
133 1 : ctx->ipid = pid;
134 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DURATION);
135 1 : if (p) ctx->duration = p->value.lfrac;
136 :
137 1 : gf_filter_pid_set_framing_mode(pid, GF_TRUE);
138 :
139 1 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_MEDIA_DATA_SIZE);
140 1 : if (!p) {
141 1 : GF_LOG(GF_LOG_WARNING, GF_LOG_AUTHOR, ("[QCP] Unknown total media size, cannot write QCP file right away\n"));
142 1 : ctx->data_size = 0;
143 : } else {
144 0 : ctx->data_size = (u32) p->value.longuint;
145 : }
146 1 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_NB_FRAMES);
147 1 : if (!p) {
148 1 : GF_LOG(GF_LOG_WARNING, GF_LOG_AUTHOR, ("[QCP] Unknown total number of media frames, cannot write QCP file\n"));
149 1 : ctx->nb_frames = 0;
150 : } else {
151 0 : ctx->nb_frames = (u32) p->value.uint;
152 : }
153 :
154 1 : if (!ctx->data_size || !ctx->nb_frames) {
155 1 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DISABLE_PROGRESSIVE, &PROP_UINT(GF_PID_FILE_PATCH_REPLACE) );
156 1 : ctx->needs_final_pach = GF_TRUE;
157 : }
158 :
159 : return GF_OK;
160 : }
161 :
162 2 : static void qcpmx_send_header(GF_QCPMxCtx *ctx, u32 data_size, u32 frame_count)
163 : {
164 : const GF_PropertyValue *p;
165 : Bool needs_rate_octet;
166 : char szName[80];
167 : u32 i, tot_size, size, sample_size, avg_rate;
168 : u32 block_size = 160;
169 : u32 sample_rate = 8000;
170 : GF_BitStream *bs;
171 : u8 *output;
172 : GF_FilterPacket *dst_pck;
173 :
174 2 : if (ctx->qcp_type==1) {
175 2 : ctx->qcp_rates = (unsigned int*)GF_QCELP_RATE_TO_SIZE;
176 2 : ctx->rt_cnt = GF_QCELP_RATE_TO_SIZE_NB;
177 : } else {
178 0 : ctx->qcp_rates = (unsigned int*)GF_SMV_EVRC_RATE_TO_SIZE;
179 0 : ctx->rt_cnt = GF_SMV_EVRC_RATE_TO_SIZE_NB;
180 : }
181 :
182 : /*dumps full table...*/
183 14 : for (i=0; i<ctx->rt_cnt; i++) {
184 14 : ctx->rtable[i].rate_idx = ctx->qcp_rates[2*i];
185 14 : ctx->rtable[i].pck_size = ctx->qcp_rates[2*i+1];
186 : }
187 :
188 2 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FRAME_SIZE);
189 2 : sample_size = p ? p->value.uint : block_size;
190 :
191 : /*check sample format - packetvideo doesn't include rate octet...*/
192 2 : needs_rate_octet = (ctx->codecid==GF_CODECID_EVRC_PV) ? GF_TRUE : GF_FALSE;
193 :
194 2 : if (needs_rate_octet) data_size += frame_count;
195 2 : ctx->has_qcp_pad = (data_size % 2) ? GF_TRUE : GF_FALSE;
196 :
197 2 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_BITRATE);
198 2 : if (p) avg_rate = p->value.uint;
199 0 : else avg_rate = frame_count ? 8*data_size*sample_rate/frame_count/block_size : 0;
200 :
201 : /*QLCM + fmt + vrat + data*/
202 2 : size = tot_size = 4+ 8+150 + 8+8 + 8 + data_size;
203 : /*pad is included in riff size*/
204 2 : if (ctx->has_qcp_pad) {
205 1 : tot_size++;
206 : }
207 : size += 8;
208 : size -= data_size;
209 2 : dst_pck = gf_filter_pck_new_alloc(ctx->opid, size, &output);
210 2 : if (!dst_pck) return;
211 :
212 2 : bs = gf_bs_new(output, size, GF_BITSTREAM_WRITE);
213 :
214 2 : gf_bs_write_data(bs, "RIFF", 4);
215 2 : gf_bs_write_u32_le(bs, tot_size);
216 2 : gf_bs_write_data(bs, "QLCM", 4);
217 2 : gf_bs_write_data(bs, "fmt ", 4);
218 2 : gf_bs_write_u32_le(bs, 150);/*fmt chunk size*/
219 2 : gf_bs_write_u8(bs, 1);
220 2 : gf_bs_write_u8(bs, 0);
221 2 : gf_bs_write_data(bs, ctx->GUID, 16);
222 2 : gf_bs_write_u16_le(bs, 1);
223 : memset(szName, 0, 80);
224 2 : strcpy(szName, (ctx->qcp_type==1) ? "QCELP-GPACExport" : ((ctx->qcp_type==2) ? "SMV-GPACExport" : "EVRC-GPACExport"));
225 2 : gf_bs_write_data(bs, szName, 80);
226 2 : gf_bs_write_u16_le(bs, avg_rate);
227 2 : gf_bs_write_u16_le(bs, sample_size);
228 2 : gf_bs_write_u16_le(bs, block_size);
229 2 : gf_bs_write_u16_le(bs, sample_rate);
230 2 : gf_bs_write_u16_le(bs, avg_rate);
231 2 : gf_bs_write_u32_le(bs, ctx->rt_cnt);
232 18 : for (i=0; i<8; i++) {
233 16 : if (i<ctx->rt_cnt) {
234 : /*frame size MINUS rate octet*/
235 14 : gf_bs_write_u8(bs, ctx->rtable[i].pck_size - 1);
236 14 : gf_bs_write_u8(bs, ctx->rtable[i].rate_idx);
237 : } else {
238 2 : gf_bs_write_u16(bs, 0);
239 : }
240 : }
241 : memset(szName, 0, 80);
242 2 : gf_bs_write_data(bs, szName, 20);/*reserved*/
243 2 : gf_bs_write_data(bs, "vrat", 4);
244 2 : gf_bs_write_u32_le(bs, 8);/*vrat chunk size*/
245 2 : gf_bs_write_u32_le(bs, ctx->rt_cnt);
246 2 : gf_bs_write_u32_le(bs, frame_count);
247 2 : gf_bs_write_data(bs, "data", 4);
248 2 : gf_bs_write_u32_le(bs, data_size);/*data chunk size*/
249 :
250 2 : ctx->needs_rate_byte = needs_rate_octet ? 1 : 0;
251 :
252 2 : gf_bs_del(bs);
253 :
254 2 : if (!ctx->first) {
255 : assert(ctx->needs_final_pach);
256 1 : gf_filter_pck_set_framing(dst_pck, GF_FALSE, GF_FALSE);
257 1 : gf_filter_pck_set_seek_flag(dst_pck, GF_TRUE);
258 1 : gf_filter_pck_set_byte_offset(dst_pck, 0);
259 : } else {
260 1 : gf_filter_pck_set_framing(dst_pck, GF_TRUE, GF_FALSE);
261 1 : gf_filter_pck_set_byte_offset(dst_pck, GF_FILTER_NO_BO);
262 1 : ctx->first = GF_FALSE;
263 : }
264 :
265 2 : gf_filter_pck_send(dst_pck);
266 : }
267 :
268 503 : GF_Err qcpmx_process(GF_Filter *filter)
269 : {
270 503 : GF_QCPMxCtx *ctx = gf_filter_get_udta(filter);
271 : GF_FilterPacket *pck, *dst_pck;
272 : u8 *data, *output;
273 : u32 pck_size, size;
274 :
275 503 : pck = gf_filter_pid_get_packet(ctx->ipid);
276 503 : if (!pck) {
277 2 : if (gf_filter_pid_is_eos(ctx->ipid)) {
278 1 : if (ctx->needs_final_pach) {
279 1 : qcpmx_send_header(ctx, ctx->data_size, ctx->nb_frames);
280 1 : ctx->needs_final_pach = GF_FALSE;
281 : }
282 1 : if (ctx->has_qcp_pad) {
283 1 : dst_pck = gf_filter_pck_new_alloc(ctx->opid, 1, &output);
284 1 : if (dst_pck) {
285 1 : output[0] = 0;
286 1 : gf_filter_pck_set_framing(dst_pck, GF_FALSE, GF_TRUE);
287 1 : ctx->has_qcp_pad = GF_FALSE;
288 1 : gf_filter_pck_send(dst_pck);
289 : }
290 : }
291 1 : gf_filter_pid_set_eos(ctx->opid);
292 1 : return GF_EOS;
293 : }
294 : return GF_OK;
295 : }
296 :
297 501 : if (ctx->first) {
298 1 : qcpmx_send_header(ctx, ctx->data_size, ctx->nb_frames);
299 : }
300 :
301 501 : data = (char *) gf_filter_pck_get_data(pck, &pck_size);
302 :
303 501 : size = pck_size;
304 501 : ctx->data_size += pck_size;
305 501 : ctx->nb_frames ++;
306 :
307 : /*fix rate octet for QCP*/
308 501 : if (ctx->needs_rate_byte) {
309 : u32 j;
310 : u32 rate_found = 0;
311 0 : size ++;
312 :
313 0 : for (j=0; j<ctx->rt_cnt; j++) {
314 0 : if (ctx->qcp_rates[2*j+1] == pck_size) {
315 0 : rate_found = ctx->qcp_rates[2*j];
316 0 : break;
317 : }
318 : }
319 0 : if (!rate_found) {
320 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[QCP] Frame size %d not in rate table, ignoring frame\n", pck_size));
321 0 : gf_filter_pid_drop_packet(ctx->ipid);
322 0 : return GF_NON_COMPLIANT_BITSTREAM;
323 : }
324 :
325 0 : dst_pck = gf_filter_pck_new_alloc(ctx->opid, size, &output);
326 0 : if (dst_pck) {
327 0 : output[0] = rate_found;
328 0 : memcpy(output+1, data, pck_size);
329 : }
330 : } else {
331 : //send the complete data
332 501 : dst_pck = gf_filter_pck_new_ref(ctx->opid, 0, size, pck);
333 : }
334 501 : if (!dst_pck) return GF_OUT_OF_MEM;
335 :
336 501 : gf_filter_pck_merge_properties(pck, dst_pck);
337 501 : gf_filter_pck_set_byte_offset(dst_pck, GF_FILTER_NO_BO);
338 :
339 501 : gf_filter_pck_set_framing(dst_pck, ctx->first, GF_FALSE);
340 501 : ctx->first = GF_FALSE;
341 :
342 501 : gf_filter_pck_send(dst_pck);
343 :
344 501 : if (ctx->exporter) {
345 0 : u32 timescale = gf_filter_pck_get_timescale(pck);
346 0 : u64 ts = gf_filter_pck_get_cts(pck);
347 0 : gf_set_progress("Exporting", ts*ctx->duration.den, ctx->duration.num*timescale);
348 : }
349 :
350 501 : gf_filter_pid_drop_packet(ctx->ipid);
351 :
352 501 : return GF_OK;
353 : }
354 :
355 : static const GF_FilterCapability QCPMxCaps[] =
356 : {
357 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
358 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_QCELP),
359 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_EVRC),
360 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_EVRC_PV),
361 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_SMV),
362 : CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
363 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
364 : CAP_STRING(GF_CAPS_OUTPUT, GF_PROP_PID_FILE_EXT, "qcp"),
365 : CAP_STRING(GF_CAPS_OUTPUT, GF_PROP_PID_MIME, "audio/qcp"),
366 : };
367 :
368 :
369 : #define OFFS(_n) #_n, offsetof(GF_QCPMxCtx, _n)
370 : static const GF_FilterArgs QCPMxArgs[] =
371 : {
372 : { OFFS(exporter), "compatibility with old exporter, displays export results", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
373 : {0}
374 : };
375 :
376 :
377 : GF_FilterRegister QCPMxRegister = {
378 : .name = "writeqcp",
379 : GF_FS_SET_DESCRIPTION("QCP writer")
380 : GF_FS_SET_HELP("This filter converts a single stream to a QCP output file.")
381 : .private_size = sizeof(GF_QCPMxCtx),
382 : .args = QCPMxArgs,
383 : SETCAPS(QCPMxCaps),
384 : .configure_pid = qcpmx_configure_pid,
385 : .process = qcpmx_process
386 : };
387 :
388 :
389 2877 : const GF_FilterRegister *qcpmx_register(GF_FilterSession *session)
390 : {
391 2877 : return &QCPMxRegister;
392 : }
|