Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2018-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / RAW PCM 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 : typedef struct
31 : {
32 : //opts
33 : u32 framelen, safmt, sr, ch;
34 :
35 : //only one input pid declared
36 : GF_FilterPid *ipid;
37 : //only one output pid declared
38 : GF_FilterPid *opid;
39 :
40 : Bool file_loaded, is_playing, initial_play_done;
41 : u64 cts;
42 : u32 frame_size, nb_bytes_in_frame, Bps;
43 : u64 filepos, total_frames;
44 : GF_FilterPacket *out_pck;
45 : u8 *out_data;
46 : Bool reverse_play, done;
47 : } GF_PCMReframeCtx;
48 :
49 :
50 :
51 :
52 34 : GF_Err pcmreframe_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
53 : {
54 : const GF_PropertyValue *p;
55 34 : GF_PCMReframeCtx *ctx = gf_filter_get_udta(filter);
56 :
57 34 : if (is_remove) {
58 0 : ctx->ipid = NULL;
59 0 : if (ctx->opid) {
60 0 : gf_filter_pid_remove(ctx->opid);
61 0 : ctx->opid = NULL;
62 : }
63 : return GF_OK;
64 : }
65 34 : if (! gf_filter_pid_check_caps(pid))
66 : return GF_NOT_SUPPORTED;
67 :
68 34 : ctx->ipid = pid;
69 34 : if (!ctx->safmt) {
70 34 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILE_EXT);
71 34 : if (p && p->value.string) ctx->safmt = gf_audio_fmt_parse(p->value.string);
72 : }
73 34 : if (!ctx->safmt) {
74 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[PCMReframe] Missing audio format, cannot parse\n"));
75 : return GF_BAD_PARAM;
76 : }
77 34 : if (!ctx->sr) {
78 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[PCMReframe] Missing audio sample rate, cannot parse\n"));
79 : return GF_BAD_PARAM;
80 : }
81 34 : if (!ctx->ch) {
82 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[PCMReframe] Missing audio ch, cannot parse\n"));
83 : return GF_BAD_PARAM;
84 : }
85 34 : if (!ctx->framelen) {
86 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[PCMReframe] Missing audio framelen, using 1024\n"));
87 0 : ctx->framelen = 1024;
88 : }
89 :
90 34 : ctx->Bps = gf_audio_fmt_bit_depth(ctx->safmt) / 8;
91 34 : ctx->frame_size = ctx->framelen * ctx->Bps * ctx->ch;
92 :
93 34 : if (!ctx->opid)
94 34 : ctx->opid = gf_filter_pid_new(filter);
95 :
96 34 : gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
97 34 : gf_filter_pid_set_framing_mode(ctx->ipid, GF_FALSE);
98 34 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_AUDIO));
99 34 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, &PROP_UINT(GF_CODECID_RAW));
100 34 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLE_RATE, &PROP_UINT(ctx->sr));
101 34 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_NUM_CHANNELS, &PROP_UINT(ctx->ch));
102 34 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLES_PER_FRAME, &PROP_UINT(ctx->framelen));
103 34 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_AUDIO_FORMAT, &PROP_UINT(ctx->safmt));
104 34 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_TIMESCALE, &PROP_UINT(ctx->sr));
105 34 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PLAYBACK_MODE, &PROP_UINT(GF_PLAYBACK_MODE_REWIND));
106 34 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CAN_DATAREF, &PROP_BOOL(GF_TRUE));
107 :
108 34 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILE_CACHED);
109 34 : if (p && p->value.boolean) ctx->file_loaded = GF_TRUE;
110 :
111 34 : if (!gf_sys_is_test_mode() ) {
112 0 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_BITRATE, & PROP_UINT(ctx->sr * ctx->Bps * ctx->ch));
113 : }
114 :
115 34 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_DOWN_SIZE);
116 34 : if (p && p->value.longuint) {
117 : u64 nb_frames = p->value.longuint;
118 34 : nb_frames /= ctx->Bps * ctx->ch;
119 : ctx->total_frames = p->value.longuint;
120 34 : ctx->total_frames /= ctx->frame_size;
121 :
122 34 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, &PROP_FRAC64_INT(nb_frames, ctx->sr));
123 : }
124 :
125 : return GF_OK;
126 : }
127 :
128 2336 : static Bool pcmreframe_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
129 : {
130 : u32 nb_frames;
131 : GF_FilterEvent fevt;
132 2336 : GF_PCMReframeCtx *ctx = gf_filter_get_udta(filter);
133 :
134 2336 : switch (evt->base.type) {
135 34 : case GF_FEVT_PLAY:
136 34 : if (!ctx->is_playing) {
137 34 : ctx->is_playing = GF_TRUE;
138 34 : ctx->cts = 0;
139 : }
140 34 : ctx->done = GF_FALSE;
141 :
142 34 : if (!ctx->total_frames)
143 : return GF_TRUE;
144 :
145 34 : if (evt->play.start_range>=0) {
146 34 : ctx->cts = (u64) (evt->play.start_range * ctx->sr);
147 : } else {
148 0 : ctx->cts = (ctx->total_frames-1) * ctx->framelen;
149 : }
150 34 : nb_frames = (u32) (ctx->cts / ctx->framelen);
151 34 : if (nb_frames==ctx->total_frames) {
152 2 : if (evt->play.speed>=0) {
153 0 : ctx->done = GF_TRUE;
154 0 : return GF_TRUE;
155 : }
156 2 : nb_frames--;
157 2 : ctx->cts = nb_frames * ctx->framelen;
158 : }
159 :
160 34 : ctx->filepos = nb_frames * ctx->frame_size;
161 34 : ctx->reverse_play = (evt->play.speed<0) ? GF_TRUE : GF_FALSE;
162 :
163 34 : if (!ctx->initial_play_done) {
164 34 : ctx->initial_play_done = GF_TRUE;
165 : //seek will not change the current source state, don't send a seek
166 34 : if (!ctx->filepos)
167 : return GF_TRUE;
168 : }
169 : //post a seek
170 2 : GF_FEVT_INIT(fevt, GF_FEVT_SOURCE_SEEK, ctx->ipid);
171 2 : fevt.seek.start_offset = ctx->filepos;
172 2 : gf_filter_pid_send_event(ctx->ipid, &fevt);
173 :
174 : //cancel event
175 2 : return GF_TRUE;
176 :
177 0 : case GF_FEVT_STOP:
178 : //don't cancel event
179 0 : ctx->is_playing = GF_FALSE;
180 0 : return GF_FALSE;
181 :
182 : case GF_FEVT_SET_SPEED:
183 : //cancel event
184 : return GF_TRUE;
185 : default:
186 : break;
187 : }
188 : //by default don't cancel event - to rework once we have downloading in place
189 2302 : return GF_FALSE;
190 : }
191 :
192 2326 : void pcmreframe_flush_packet(GF_PCMReframeCtx *ctx)
193 : {
194 2326 : if (ctx->reverse_play) {
195 36 : u32 i, nb_bytes_in_sample, nb_samples = ctx->nb_bytes_in_frame / ctx->Bps / ctx->ch;
196 36 : nb_bytes_in_sample = ctx->Bps * ctx->ch;
197 18468 : for (i=0; i<nb_samples/2; i++) {
198 : char store[100];
199 18432 : memcpy(store, ctx->out_data + i*nb_bytes_in_sample, nb_bytes_in_sample);
200 18432 : memcpy(ctx->out_data + i*nb_bytes_in_sample, ctx->out_data + (nb_samples - i - 1)*nb_bytes_in_sample, nb_bytes_in_sample);
201 18432 : memcpy(ctx->out_data + (nb_samples-i-1)*nb_bytes_in_sample, store, nb_bytes_in_sample);
202 : }
203 : }
204 2326 : gf_filter_pck_send(ctx->out_pck);
205 2326 : ctx->out_pck = NULL;
206 2326 : }
207 2430 : GF_Err pcmreframe_process(GF_Filter *filter)
208 : {
209 2430 : GF_PCMReframeCtx *ctx = gf_filter_get_udta(filter);
210 : GF_FilterPacket *pck;
211 : u64 byte_offset;
212 : u8 *data;
213 : u32 pck_size;
214 :
215 2430 : if (ctx->done) return GF_EOS;
216 :
217 2428 : if (!ctx->is_playing && ctx->opid) return GF_OK;
218 :
219 2362 : pck = gf_filter_pid_get_packet(ctx->ipid);
220 2362 : if (!pck) {
221 60 : if (gf_filter_pid_is_eos(ctx->ipid) && !ctx->reverse_play) {
222 35 : if (ctx->out_pck) {
223 0 : gf_filter_pck_truncate(ctx->out_pck, ctx->nb_bytes_in_frame);
224 0 : gf_filter_pck_set_duration(ctx->out_pck, ctx->nb_bytes_in_frame/ctx->Bps/ctx->ch);
225 0 : pcmreframe_flush_packet(ctx);
226 : }
227 35 : if (ctx->opid)
228 35 : gf_filter_pid_set_eos(ctx->opid);
229 : return GF_EOS;
230 : }
231 : return GF_OK;
232 : }
233 :
234 2302 : data = (char *) gf_filter_pck_get_data(pck, &pck_size);
235 2302 : byte_offset = gf_filter_pck_get_byte_offset(pck);
236 :
237 9126 : while (pck_size) {
238 4558 : if (!ctx->out_pck) {
239 2326 : ctx->out_pck = gf_filter_pck_new_alloc(ctx->opid, ctx->frame_size, &ctx->out_data);
240 2326 : if (!ctx->out_pck) return GF_OUT_OF_MEM;
241 :
242 2326 : gf_filter_pck_set_cts(ctx->out_pck, ctx->cts);
243 2326 : gf_filter_pck_set_sap(ctx->out_pck, GF_FILTER_SAP_1);
244 2326 : gf_filter_pck_set_duration(ctx->out_pck, ctx->framelen);
245 2326 : gf_filter_pck_set_byte_offset(ctx->out_pck, byte_offset);
246 : }
247 :
248 4558 : if (pck_size + ctx->nb_bytes_in_frame < ctx->frame_size) {
249 2232 : memcpy(ctx->out_data + ctx->nb_bytes_in_frame, data, pck_size);
250 2232 : ctx->nb_bytes_in_frame += pck_size;
251 2232 : pck_size = 0;
252 : } else {
253 2326 : u32 remain = ctx->frame_size - ctx->nb_bytes_in_frame;
254 2326 : memcpy(ctx->out_data + ctx->nb_bytes_in_frame, data, remain);
255 2326 : ctx->nb_bytes_in_frame = ctx->frame_size;
256 2326 : pcmreframe_flush_packet(ctx);
257 :
258 2326 : pck_size -= remain;
259 2326 : data += remain;
260 2326 : byte_offset += remain;
261 2326 : ctx->out_pck = NULL;
262 2326 : ctx->nb_bytes_in_frame = 0;
263 :
264 : //reverse playback, the remaining data is for the next frame, we want the previous one.
265 : //Trash packet and seek to previous frame
266 2326 : if (ctx->reverse_play) {
267 : GF_FilterEvent fevt;
268 36 : if (!ctx->cts) {
269 2 : if (ctx->opid)
270 2 : gf_filter_pid_set_eos(ctx->opid);
271 2 : GF_FEVT_INIT(fevt, GF_FEVT_STOP, ctx->ipid);
272 2 : gf_filter_pid_send_event(ctx->ipid, &fevt);
273 2 : ctx->done = GF_TRUE;
274 2 : return GF_EOS;
275 : }
276 34 : ctx->cts -= ctx->framelen;
277 34 : ctx->filepos -= ctx->frame_size;
278 34 : gf_filter_pid_drop_packet(ctx->ipid);
279 : //post a seek, this will trash remaining packets in buffers
280 34 : GF_FEVT_INIT(fevt, GF_FEVT_SOURCE_SEEK, ctx->ipid);
281 34 : fevt.seek.start_offset = ctx->filepos;
282 34 : gf_filter_pid_send_event(ctx->ipid, &fevt);
283 34 : return GF_OK;
284 : }
285 2290 : ctx->cts += ctx->framelen;
286 : }
287 : }
288 2266 : gf_filter_pid_drop_packet(ctx->ipid);
289 2266 : return GF_OK;
290 : }
291 :
292 :
293 : static GF_FilterCapability PCMReframeCaps[] =
294 : {
295 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
296 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "pcm"),
297 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "audio/x-pcm"),
298 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
299 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_RAW),
300 : CAP_UINT(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
301 : };
302 :
303 : #define OFFS(_n) #_n, offsetof(GF_PCMReframeCtx, _n)
304 : static GF_FilterArgs PCMReframeArgs[] =
305 : {
306 : { OFFS(sr), "sample rate", GF_PROP_UINT, "44100", NULL, 0},
307 : { OFFS(safmt), "audio format", GF_PROP_PCMFMT, "none", NULL, 0},
308 : { OFFS(ch), "number of channels", GF_PROP_UINT, "2", NULL, 0},
309 : { OFFS(framelen), "number of samples to put in one audio frame. For planar formats, indicate plane size in samples", GF_PROP_UINT, "1024", NULL, GF_FS_ARG_HINT_ADVANCED},
310 : {0}
311 : };
312 :
313 :
314 : GF_FilterRegister PCMReframeRegister = {
315 : .name = "rfpcm",
316 : GF_FS_SET_DESCRIPTION("PCM reframer")
317 : GF_FS_SET_HELP("This filter parses raw PCM file/data and outputs corresponding raw audio PID and frames.")
318 : .private_size = sizeof(GF_PCMReframeCtx),
319 : .args = PCMReframeArgs,
320 : SETCAPS(PCMReframeCaps),
321 : .configure_pid = pcmreframe_configure_pid,
322 : .process = pcmreframe_process,
323 : .process_event = pcmreframe_process_event
324 : };
325 :
326 :
327 2877 : const GF_FilterRegister *pcmreframe_register(GF_FilterSession *session)
328 : {
329 2877 : PCMReframeArgs[1].min_max_enum = gf_audio_fmt_all_names();
330 2877 : PCMReframeCaps[1].val.value.string = (char *) gf_audio_fmt_all_shortnames();
331 2877 : return &PCMReframeRegister;
332 : }
333 :
|