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 / XIPH OGG demux 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/list.h>
29 : #include <gpac/bitstream.h>
30 :
31 : #if !defined(GPAC_DISABLE_AV_PARSERS) && !defined(GPAC_DISABLE_OGG)
32 : #include <gpac/internal/ogg.h>
33 : #include <gpac/internal/isomedia_dev.h>
34 : //#include <ogg/ogg.h>
35 : #include <gpac/avparse.h>
36 :
37 :
38 :
39 : typedef struct
40 : {
41 : u32 streamType; /*MPEG-4 streamType*/
42 : u32 num_init_headers;
43 : u32 sample_rate, bitrate, nb_chan;
44 : u32 width, height;
45 : GF_Fraction sar;
46 :
47 : u32 theora_kgs;
48 : GF_Fraction frame_rate;
49 :
50 : u32 type;
51 : } OGGInfo;
52 :
53 : typedef struct
54 : {
55 : //only one output pid declared
56 : GF_FilterPid *opid;
57 :
58 : ogg_stream_state os;
59 : u32 serial_no;
60 : /*DSI for ogg - cf constants.h*/
61 : GF_BitStream *dsi_bs;
62 :
63 : OGGInfo info;
64 : Bool got_headers;
65 :
66 : u32 parse_headers;
67 :
68 : Bool eos_detected;
69 :
70 : u32 recomputed_ts;
71 :
72 : GF_VorbisParser *vorbis_parser;
73 :
74 : GF_OpusParser *opus_parser;
75 : } GF_OGGStream;
76 :
77 : typedef struct
78 : {
79 : Double index;
80 :
81 : //only one input pid declared
82 : GF_FilterPid *ipid;
83 :
84 : u64 file_pos, file_size;
85 : u32 global_rate;
86 : GF_Fraction64 duration;
87 : Double start_range;
88 : Bool seek_file;
89 : u32 nb_playing;
90 : Bool is_file;
91 : Bool initial_play_done, file_loaded;
92 :
93 : GF_List *streams;
94 :
95 : /*ogg ogfile state*/
96 : ogg_sync_state oy;
97 : } GF_OGGDmxCtx;
98 :
99 7 : void oggdmx_signal_eos(GF_OGGDmxCtx *ctx)
100 : {
101 : GF_OGGStream *st;
102 7 : u32 i=0;
103 26 : while ((st = (GF_OGGStream*)gf_list_enum(ctx->streams, &i))) {
104 12 : if (st->opid)
105 12 : gf_filter_pid_set_eos(st->opid);
106 : }
107 7 : }
108 :
109 3000 : static GF_OGGStream *oggdmx_find_stream_for_page(GF_OGGDmxCtx *ctx, ogg_page *oggpage)
110 : {
111 : u32 i, count;
112 3000 : count = gf_list_count(ctx->streams);
113 2290 : for (i=0; i<count; i++) {
114 5290 : GF_OGGStream *st = (GF_OGGStream*)gf_list_get(ctx->streams, i);
115 5290 : if (ogg_stream_pagein(&st->os, oggpage) == 0) return st;
116 : }
117 : return NULL;
118 : }
119 :
120 25 : static void oggdmx_get_stream_info(ogg_packet *oggpacket, OGGInfo *info)
121 : {
122 : oggpack_buffer opb;
123 :
124 : memset(info, 0, sizeof(OGGInfo));
125 :
126 : /*vorbis*/
127 25 : if ((oggpacket->bytes >= 7) && !strncmp((char *) &oggpacket->packet[1], "vorbis", 6)) {
128 11 : info->streamType = GF_STREAM_AUDIO;
129 11 : oggpack_readinit(&opb, oggpacket->packet, oggpacket->bytes);
130 11 : oggpack_adv( &opb, 88);
131 11 : info->nb_chan = oggpack_read( &opb, 8); /*nb chan*/
132 11 : info->sample_rate = oggpack_read(&opb, 32);
133 11 : oggpack_adv( &opb, 32); /*max rate*/
134 11 : info->bitrate = oggpack_read(&opb, 32);
135 11 : info->num_init_headers = 3;
136 11 : info->type = GF_CODECID_VORBIS;
137 : }
138 : /*speex*/
139 14 : else if ((oggpacket->bytes >= 7) && !strncmp((char *) &oggpacket->packet[0], "Speex", 5)) {
140 0 : info->streamType = GF_STREAM_AUDIO;
141 0 : oggpack_readinit(&opb, oggpacket->packet, oggpacket->bytes);
142 0 : oggpack_adv(&opb, 224);
143 0 : oggpack_adv(&opb, 32);
144 0 : oggpack_adv( &opb, 32);
145 0 : info->sample_rate = oggpack_read(&opb, 32);
146 0 : info->type = GF_CODECID_SPEEX;
147 0 : info->num_init_headers = 1;
148 : }
149 : /*flac*/
150 14 : else if ((oggpacket->bytes >= 4) && !strncmp((char *) &oggpacket->packet[0], "fLaC", 4)) {
151 0 : info->streamType = GF_STREAM_AUDIO;
152 0 : info->type = GF_CODECID_FLAC;
153 0 : info->num_init_headers = 3;
154 : }
155 : /*opus*/
156 14 : else if ((oggpacket->bytes >= 8) && !strncmp((char *) &oggpacket->packet[0], "OpusHead", 8)) {
157 3 : info->streamType = GF_STREAM_AUDIO;
158 3 : info->type = GF_CODECID_OPUS;
159 3 : info->num_init_headers = 1;
160 3 : info->sample_rate = 48000;
161 : }
162 : /*theora*/
163 11 : else if ((oggpacket->bytes >= 7) && !strncmp((char *) &oggpacket->packet[1], "theora", 6)) {
164 : GF_BitStream *bs;
165 : u32 keyframe_freq_force;
166 :
167 11 : info->streamType = GF_STREAM_VISUAL;
168 11 : info->type = GF_CODECID_THEORA;
169 11 : bs = gf_bs_new((char *) oggpacket->packet, oggpacket->bytes, GF_BITSTREAM_READ);
170 11 : gf_bs_read_int(bs, 56);
171 11 : gf_bs_read_int(bs, 8); /* major version num */
172 11 : gf_bs_read_int(bs, 8); /* minor version num */
173 11 : gf_bs_read_int(bs, 8); /* subminor version num */
174 11 : info->width = gf_bs_read_int(bs, 16) << 4; /* width */
175 11 : info->height = gf_bs_read_int(bs, 16) << 4; /* height */
176 11 : gf_bs_read_int(bs, 24); /* frame width */
177 11 : gf_bs_read_int(bs, 24); /* frame height */
178 11 : gf_bs_read_int(bs, 8); /* x offset */
179 11 : gf_bs_read_int(bs, 8); /* y offset */
180 11 : info->frame_rate.den = gf_bs_read_u32(bs);
181 11 : info->frame_rate.num = gf_bs_read_u32(bs);
182 11 : info->sar.num = gf_bs_read_int(bs, 24); /* aspect_numerator */
183 11 : info->sar.den =gf_bs_read_int(bs, 24); /* aspect_denominator */
184 11 : gf_bs_read_int(bs, 8); /* colorspace */
185 11 : info->bitrate = gf_bs_read_int(bs, 24);/* bitrate */
186 11 : gf_bs_read_int(bs, 6); /* quality */
187 :
188 : /*patch for compatibility with old arch*/
189 11 : if ((info->frame_rate.den==25025) && (info->frame_rate.num==1001) ) {
190 11 : info->frame_rate.den = 25000;
191 11 : info->frame_rate.num = 1000;
192 : }
193 :
194 11 : keyframe_freq_force = 1 << gf_bs_read_int(bs, 5);
195 11 : info->theora_kgs = 0;
196 11 : keyframe_freq_force--;
197 77 : while (keyframe_freq_force) {
198 66 : info->theora_kgs ++;
199 66 : keyframe_freq_force >>= 1;
200 : }
201 11 : info->num_init_headers = 3;
202 11 : gf_bs_del(bs);
203 : }
204 25 : }
205 :
206 19 : static void oggdmx_declare_pid(GF_Filter *filter, GF_OGGDmxCtx *ctx, GF_OGGStream *st)
207 : {
208 19 : if (!st->opid) {
209 19 : st->opid = gf_filter_pid_new(filter);
210 : }
211 : // gf_filter_pid_set_property(st->opid, GF_PROP_PID_ID, &PROP_UINT(st->serial_no) );
212 19 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_ID, &PROP_UINT(1 + gf_list_find(ctx->streams, st) ) );
213 19 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(st->info.streamType) );
214 19 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_CODECID, &PROP_UINT(st->info.type) );
215 19 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_BITRATE, &PROP_UINT(st->info.bitrate) );
216 19 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_TIMESCALE, &PROP_UINT(st->info.sample_rate ? st->info.sample_rate : st->info.frame_rate.den) );
217 19 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_PROFILE_LEVEL, &PROP_UINT(0xFE) );
218 :
219 : //opus DSI is formatted as box (ffmpeg compat) we might want to change that to avoid the box header
220 19 : if (st->info.type==GF_CODECID_OPUS) {
221 3 : GF_OpusSpecificBox *opus = (GF_OpusSpecificBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_DOPS);
222 3 : st->dsi_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
223 3 : opus->version = 0;
224 :
225 3 : opus->OutputChannelCount = st->opus_parser->OutputChannelCount;
226 3 : opus->PreSkip = st->opus_parser->PreSkip;
227 3 : opus->InputSampleRate = st->opus_parser->InputSampleRate;
228 3 : opus->OutputGain = st->opus_parser->OutputGain;
229 3 : opus->ChannelMappingFamily = st->opus_parser->ChannelMappingFamily;
230 3 : opus->StreamCount = st->opus_parser->StreamCount;
231 3 : opus->CoupledCount = st->opus_parser->CoupledCount;
232 3 : memcpy(opus->ChannelMapping, st->opus_parser->ChannelMapping, sizeof(char)*255);
233 3 : gf_isom_box_size((GF_Box *) opus);
234 3 : gf_isom_box_write((GF_Box *) opus, st->dsi_bs);
235 3 : gf_isom_box_del((GF_Box *) opus);
236 :
237 3 : st->info.nb_chan = st->opus_parser->OutputChannelCount;
238 : }
239 :
240 19 : if (st->dsi_bs) {
241 : u8 *data;
242 : u32 size;
243 19 : gf_bs_get_content(st->dsi_bs, &data, &size);
244 19 : gf_bs_del(st->dsi_bs);
245 19 : st->dsi_bs = NULL;
246 19 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_DECODER_CONFIG, &PROP_DATA_NO_COPY(data, size) );
247 : }
248 :
249 19 : if (st->info.sample_rate)
250 11 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_SAMPLE_RATE, &PROP_UINT(st->info.sample_rate) );
251 :
252 19 : if (st->info.nb_chan)
253 11 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_NUM_CHANNELS, &PROP_UINT(st->info.nb_chan) );
254 :
255 19 : if (st->info.width)
256 8 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_WIDTH, &PROP_UINT(st->info.width) );
257 19 : if (st->info.height)
258 8 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_HEIGHT, &PROP_UINT(st->info.height) );
259 19 : if (st->info.sar.den)
260 8 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_SAR, &PROP_FRAC(st->info.sar) );
261 19 : if (st->info.frame_rate.den)
262 8 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_FPS, &PROP_FRAC(st->info.frame_rate) );
263 :
264 19 : if (ctx->duration.num) {
265 6 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_DURATION, & PROP_FRAC64(ctx->duration));
266 6 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_PLAYBACK_MODE, &PROP_UINT(GF_PLAYBACK_MODE_FASTFORWARD ) );
267 : }
268 19 : }
269 :
270 19 : static void oggdmx_new_stream(GF_Filter *filter, GF_OGGDmxCtx *ctx, ogg_page *oggpage)
271 : {
272 : ogg_packet oggpacket;
273 : u32 serial_no, i;
274 : GF_OGGStream *st;
275 :
276 : /*reannounce of stream (caroussel in live streams) */
277 19 : serial_no = ogg_page_serialno(oggpage);
278 19 : i=0;
279 46 : while ((st = (GF_OGGStream*)gf_list_enum(ctx->streams, &i))) {
280 8 : if (st->serial_no==serial_no) {
281 : //resetup stream
282 0 : ogg_stream_clear(&st->os);
283 0 : ogg_stream_init(&st->os, st->serial_no);
284 0 : ogg_stream_pagein(&st->os, oggpage);
285 0 : st->parse_headers = st->info.num_init_headers;
286 0 : return;
287 : }
288 : }
289 :
290 : /*look if we have the same stream defined (eg, reuse first stream dead with same header page)*/
291 19 : i=0;
292 46 : while ((st = (GF_OGGStream*)gf_list_enum(ctx->streams, &i))) {
293 8 : if (st->eos_detected) {
294 0 : gf_filter_pid_set_eos(st->opid);
295 : //and reuse the pid connection
296 0 : break;
297 : }
298 : }
299 19 : if (!st) {
300 19 : GF_SAFEALLOC(st, GF_OGGStream);
301 19 : if (!st) {
302 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[OGG] Failed to allocate stream for demux\n"));
303 : return;
304 : }
305 : }
306 19 : st->eos_detected = GF_FALSE;
307 19 : st->serial_no = serial_no;
308 19 : ogg_stream_init(&st->os, st->serial_no);
309 19 : ogg_stream_pagein(&st->os, oggpage);
310 :
311 19 : ogg_stream_packetpeek(&st->os, &oggpacket);
312 19 : oggdmx_get_stream_info(&oggpacket, &st->info);
313 :
314 19 : gf_list_add(ctx->streams, st);
315 19 : st->parse_headers = st->info.num_init_headers;
316 19 : switch (st->info.type) {
317 8 : case GF_CODECID_VORBIS:
318 8 : GF_SAFEALLOC(st->vorbis_parser, GF_VorbisParser);
319 : break;
320 3 : case GF_CODECID_OPUS:
321 3 : GF_SAFEALLOC(st->opus_parser, GF_OpusParser);
322 : break;
323 : default:
324 : break;
325 : }
326 :
327 19 : if (st->got_headers) {
328 0 : oggdmx_declare_pid(filter, ctx, st);
329 : }
330 19 : i=0;
331 19 : ctx->global_rate = 0;
332 65 : while ((st = (GF_OGGStream*)gf_list_enum(ctx->streams, &i))) {
333 27 : if (!st->eos_detected) ctx->global_rate += st->info.bitrate;
334 : }
335 19 : if (ctx->global_rate && ctx->is_file && !ctx->file_loaded) {
336 0 : if (!ctx->file_size) {
337 0 : GF_PropertyEntry *pe=NULL;
338 0 : const GF_PropertyValue *p = gf_filter_pid_get_info(ctx->ipid, GF_PROP_PID_DOWN_SIZE, &pe);
339 0 : if (p) ctx->file_size = p->value.longuint;
340 0 : gf_filter_release_property(pe);
341 : }
342 0 : if (ctx->file_size) {
343 0 : ctx->duration.num = (u32) (8 * ctx->file_size);
344 0 : ctx->duration.den = ctx->global_rate;
345 : }
346 : }
347 : }
348 :
349 19 : GF_Err oggdmx_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
350 : {
351 : u32 i;
352 19 : GF_OGGDmxCtx *ctx = gf_filter_get_udta(filter);
353 :
354 19 : if (is_remove) {
355 : GF_OGGStream *st;
356 0 : ctx->ipid = NULL;
357 :
358 0 : while ((st = (GF_OGGStream*)gf_list_enum(ctx->streams, &i))) {
359 0 : if (st->opid)
360 0 : gf_filter_pid_remove(st->opid);
361 : }
362 : return GF_OK;
363 : }
364 19 : if (! gf_filter_pid_check_caps(pid))
365 : return GF_NOT_SUPPORTED;
366 :
367 19 : ctx->ipid = pid;
368 19 : return GF_OK;
369 : }
370 :
371 3167 : static void oggdmx_check_dur(GF_Filter *filter, GF_OGGDmxCtx *ctx)
372 : {
373 : ogg_sync_state oy;
374 : FILE *stream;
375 : const GF_PropertyValue *p;
376 : OGGInfo info, the_info;
377 : ogg_page oggpage;
378 : ogg_packet oggpacket;
379 : ogg_stream_state os, the_os;
380 : u64 max_gran;
381 : Bool has_stream = GF_FALSE;
382 : GF_VorbisParser vp;
383 : GF_OpusParser op;
384 : u64 recompute_ts;
385 : GF_Fraction64 dur;
386 :
387 6331 : if (!ctx->index || ctx->duration.num) return;
388 :
389 3 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILE_CACHED);
390 3 : if (p && p->value.boolean) ctx->file_loaded = GF_TRUE;
391 :
392 3 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILEPATH);
393 3 : if (!p || !p->value.string || !strncmp(p->value.string, "gmem://", 7) ) {
394 0 : ctx->is_file = GF_FALSE;
395 0 : ctx->duration.num=1;
396 : return;
397 : }
398 3 : ctx->is_file = GF_TRUE;
399 3 : if (!ctx->file_loaded) return;
400 :
401 3 : stream = gf_fopen(p->value.string, "rb");
402 3 : if (!stream) return;
403 :
404 3 : ogg_sync_init(&oy);
405 : memset(&the_info, 0, sizeof(OGGInfo));
406 : memset(&vp, 0, sizeof(GF_VorbisParser));
407 : recompute_ts = 0;
408 : max_gran = 0;
409 : while (1) {
410 : char buf[10000];
411 2431 : while (ogg_sync_pageout(&oy, &oggpage) != 1 ) {
412 : char *buffer;
413 : u32 bytes;
414 :
415 892 : if (gf_feof(stream))
416 : break;
417 :
418 892 : bytes = (u32) gf_fread(buf, 10000, stream);
419 892 : if (!bytes) break;
420 892 : buffer = ogg_sync_buffer(&oy, bytes);
421 : memcpy(buffer, buf, bytes);
422 892 : ogg_sync_wrote(&oy, bytes);
423 : }
424 1539 : if (gf_feof(stream))
425 : break;
426 :
427 1536 : if (ogg_page_bos(&oggpage)) {
428 6 : ogg_stream_init(&os, ogg_page_serialno(&oggpage));
429 6 : if (ogg_stream_pagein(&os, &oggpage) >= 0 ) {
430 6 : ogg_stream_packetpeek(&os, &oggpacket);
431 6 : if (ogg_stream_pagein(&os, &oggpage) >= 0 ) {
432 6 : ogg_stream_packetpeek(&os, &oggpacket);
433 6 : oggdmx_get_stream_info(&oggpacket, &info);
434 : }
435 6 : if (!has_stream) {
436 : has_stream = GF_TRUE;
437 3 : ogg_stream_init(&the_os, ogg_page_serialno(&oggpage));
438 3 : the_info = info;
439 : }
440 : }
441 6 : ogg_stream_clear(&os);
442 : }
443 1536 : if (has_stream && (ogg_stream_pagein(&the_os, &oggpage) >= 0) ) {
444 8675 : while (ogg_stream_packetout(&the_os, &oggpacket ) > 0 ) {
445 8318 : if (the_info.type==GF_CODECID_VORBIS) {
446 8318 : if (the_info.num_init_headers) {
447 9 : the_info.num_init_headers--;
448 9 : gf_vorbis_parse_header(&vp, oggpacket.packet, oggpacket.bytes);
449 : } else {
450 8309 : recompute_ts += gf_vorbis_check_frame(&vp, (char *) oggpacket.packet, oggpacket.bytes);
451 : }
452 0 : } else if (the_info.type==GF_CODECID_OPUS) {
453 0 : if (the_info.num_init_headers) {
454 0 : the_info.num_init_headers--;
455 0 : gf_opus_parse_header(&op, oggpacket.packet, oggpacket.bytes);
456 : } else {
457 0 : recompute_ts += gf_opus_check_frame(&op, (char *) oggpacket.packet, oggpacket.bytes);
458 : }
459 :
460 0 : } else if ((oggpacket.granulepos>=0) && ((u64) oggpacket.granulepos>max_gran) ) {
461 : max_gran = oggpacket.granulepos;
462 : }
463 : }
464 : }
465 : }
466 3 : ogg_sync_clear(&oy);
467 3 : ctx->file_size = gf_ftell(stream);
468 3 : if (has_stream) {
469 3 : ogg_stream_clear(&the_os);
470 3 : if (recompute_ts) {
471 3 : dur.num = (u32) recompute_ts;
472 3 : dur.den = the_info.sample_rate;
473 : } else {
474 : //convert granule to time
475 0 : if (the_info.sample_rate) {
476 0 : dur.num = (s32) max_gran;
477 0 : } else if (the_info.frame_rate.num) {
478 0 : s64 iframe = max_gran >> the_info.theora_kgs;
479 0 : s64 pframe = max_gran - (iframe << the_info.theora_kgs);
480 0 : pframe += iframe;
481 0 : dur.num = (s32) (pframe / the_info.frame_rate.num);
482 : } else {
483 : dur.num = 0;
484 : }
485 0 : if (the_info.sample_rate) dur.den = the_info.sample_rate;
486 0 : else dur.den = the_info.frame_rate.den;
487 : }
488 :
489 3 : if (!ctx->duration.num || (ctx->duration.num * dur.den != dur.num * ctx->duration.den)) {
490 3 : u32 i=0;
491 : GF_OGGStream *st;
492 3 : ctx->duration = dur;
493 3 : while ( (st = gf_list_enum(ctx->streams, &i)) ) {
494 0 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_DURATION, & PROP_FRAC64(ctx->duration));
495 : }
496 : }
497 : }
498 3 : gf_fclose(stream);
499 : }
500 :
501 3172 : static Bool oggdmx_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
502 : {
503 : u32 i;
504 : GF_OGGStream *st;
505 : GF_FilterEvent fevt;
506 3172 : GF_OGGDmxCtx *ctx = gf_filter_get_udta(filter);
507 :
508 3172 : switch (evt->base.type) {
509 19 : case GF_FEVT_PLAY:
510 19 : if (ctx->nb_playing && (ctx->start_range == evt->play.start_range)) {
511 8 : ctx->nb_playing++;
512 8 : return GF_TRUE;
513 : }
514 11 : ctx->nb_playing++;
515 11 : if (! ctx->is_file) {
516 : return GF_FALSE;
517 : }
518 3 : oggdmx_check_dur(filter, ctx);
519 :
520 :
521 3 : ctx->start_range = evt->play.start_range;
522 3 : ctx->file_pos = 0;
523 3 : if (ctx->duration.num) {
524 3 : ctx->file_pos = (u32) (ctx->file_size * ctx->start_range);
525 3 : ctx->file_pos *= ctx->duration.den;
526 3 : ctx->file_pos /= ctx->duration.num;
527 3 : if (ctx->file_pos>ctx->file_size) return GF_TRUE;
528 : }
529 :
530 3 : if (!ctx->initial_play_done) {
531 3 : ctx->initial_play_done = GF_TRUE;
532 : //seek will not change the current source state, don't send a seek
533 3 : if (!ctx->file_pos)
534 : return GF_TRUE;
535 : }
536 0 : ctx->seek_file = GF_TRUE;
537 0 : i=0;
538 0 : while ((st = gf_list_enum(ctx->streams, &i)) ) {
539 0 : if (st->info.sample_rate) {
540 0 : st->recomputed_ts = (u32) (ctx->start_range * st->info.sample_rate);
541 : } else {
542 0 : st->recomputed_ts = (u32) (ctx->start_range * st->info.frame_rate.den);
543 : }
544 : }
545 :
546 : //post a seek
547 0 : GF_FEVT_INIT(fevt, GF_FEVT_SOURCE_SEEK, ctx->ipid);
548 0 : fevt.seek.start_offset = ctx->file_pos;
549 0 : gf_filter_pid_send_event(ctx->ipid, &fevt);
550 :
551 : //cancel event
552 0 : return GF_TRUE;
553 :
554 7 : case GF_FEVT_STOP:
555 7 : ctx->nb_playing --;
556 : //cancel event if not last stream
557 7 : if (ctx->nb_playing) return GF_TRUE;
558 :
559 : //cancel event if we didn't get all stream headers yet not last stream
560 4 : i=0;
561 15 : while ((st = gf_list_enum(ctx->streams, &i))) {
562 7 : if (!st->got_headers) return GF_TRUE;
563 : }
564 : return GF_FALSE;
565 :
566 : case GF_FEVT_SET_SPEED:
567 : //cancel event
568 : return GF_TRUE;
569 : default:
570 : break;
571 : }
572 : //by default don't cancel event - to rework once we have downloading in place
573 3146 : return GF_FALSE;
574 : }
575 :
576 3164 : GF_Err oggdmx_process(GF_Filter *filter)
577 : {
578 : ogg_page oggpage;
579 3164 : GF_OGGDmxCtx *ctx = gf_filter_get_udta(filter);
580 : GF_FilterPacket *pck;
581 : GF_OGGStream *st;
582 : s64 granulepos_init = -1;
583 :
584 : //update duration
585 3164 : oggdmx_check_dur(filter, ctx);
586 :
587 :
588 3164 : if (ctx->seek_file) {
589 0 : ogg_sync_clear(&ctx->oy);
590 0 : ogg_sync_init(&ctx->oy);
591 0 : ctx->seek_file = GF_FALSE;
592 : } else {
593 3164 : u32 i=0;
594 : u32 would_block = 0;
595 : //check if all the streams are in block state, if so return.
596 : //we need to check for all output since one pid could still be buffering
597 12482 : while ((st = gf_list_enum(ctx->streams, &i))) {
598 6154 : if (st->got_headers && gf_filter_pid_would_block(st->opid))
599 2136 : would_block++;
600 : }
601 3164 : if (would_block && (would_block+1==i))
602 0 : return GF_OK;
603 : }
604 :
605 : while (1) {
606 : ogg_packet oggpacket;
607 :
608 9328 : if (ogg_sync_pageout(&ctx->oy, &oggpage ) != 1 ) {
609 : u32 pck_size;
610 : char *data, *buffer;
611 :
612 6309 : pck = gf_filter_pid_get_packet(ctx->ipid);
613 6309 : if (!pck) {
614 3164 : if (gf_filter_pid_is_eos(ctx->ipid)) oggdmx_signal_eos(ctx);
615 3164 : return GF_OK;
616 : }
617 3145 : data = (char *) gf_filter_pck_get_data(pck, &pck_size);
618 3145 : buffer = ogg_sync_buffer(&ctx->oy, pck_size);
619 3145 : memcpy(buffer, data, pck_size);
620 3145 : if (ogg_sync_wrote(&ctx->oy, pck_size) >= 0) {
621 3145 : gf_filter_pid_drop_packet(ctx->ipid);
622 : }
623 3145 : continue;
624 : }
625 :
626 3019 : if (ogg_page_bos(&oggpage)) {
627 19 : oggdmx_new_stream(filter, ctx, &oggpage);
628 19 : continue;
629 : }
630 :
631 3000 : st = oggdmx_find_stream_for_page(ctx, &oggpage);
632 3000 : if (!st) {
633 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[OGG] cannot find stream for ogg page\n"));
634 0 : continue;
635 : }
636 :
637 3000 : if (ogg_page_eos(&oggpage))
638 12 : st->eos_detected = GF_TRUE;
639 :
640 23102 : while (ogg_stream_packetout(&st->os, &oggpacket ) > 0 ) {
641 20153 : if (st->parse_headers && !st->got_headers) {
642 : Bool res = GF_FALSE;
643 : Bool add_page = GF_FALSE;
644 51 : u32 bytes = oggpacket.bytes;
645 : //bug in some files where first header is repeated
646 51 : if ( (st->parse_headers + 1 == st->info.num_init_headers) && st->dsi_bs && (gf_bs_get_position(st->dsi_bs) == 2 + bytes) )
647 0 : continue;
648 :
649 51 : switch (st->info.type) {
650 24 : case GF_CODECID_VORBIS:
651 24 : res = gf_vorbis_parse_header(st->vorbis_parser, (char *) oggpacket.packet, oggpacket.bytes);
652 24 : if (!res) {
653 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[OGG] Failed to parse Vorbis header\n"));
654 : } else {
655 : add_page = GF_TRUE;
656 : }
657 : break;
658 3 : case GF_CODECID_OPUS:
659 3 : res = gf_opus_parse_header(st->opus_parser, (char *) oggpacket.packet, oggpacket.bytes);
660 3 : if (!res) {
661 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[OGG] Failed to parse Opus header\n"));
662 : }
663 : break;
664 : case GF_CODECID_THEORA:
665 : add_page = GF_TRUE;
666 : break;
667 : }
668 :
669 : if (add_page) {
670 48 : if (!st->dsi_bs) st->dsi_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
671 48 : gf_bs_write_u16(st->dsi_bs, oggpacket.bytes);
672 48 : gf_bs_write_data(st->dsi_bs, (char *) oggpacket.packet, oggpacket.bytes);
673 : }
674 :
675 51 : st->parse_headers--;
676 51 : if (!st->parse_headers) {
677 19 : st->got_headers = GF_TRUE;
678 19 : oggdmx_declare_pid(filter, ctx, st);
679 : }
680 :
681 51 : granulepos_init = oggpacket.granulepos;
682 20051 : } else if (st->parse_headers && st->got_headers) {
683 0 : st->parse_headers--;
684 20051 : } else if (!st->opid) {
685 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[OGG] Channel %d packet before configure done - discarding\n", st->serial_no));
686 : } else {
687 : u8 *output;
688 : GF_FilterPacket *dst_pck;
689 :
690 20051 : if (st->info.type==GF_CODECID_THEORA) {
691 : oggpack_buffer opb;
692 5120 : oggpackB_readinit(&opb, oggpacket.packet, oggpacket.bytes);
693 : /*not a new frame*/
694 5120 : if (oggpackB_read(&opb, 1) != 0) continue;
695 :
696 5116 : dst_pck = gf_filter_pck_new_alloc(st->opid, oggpacket.bytes, &output);
697 5116 : if (!dst_pck) return GF_OUT_OF_MEM;
698 :
699 5116 : memcpy(output, (char *) oggpacket.packet, oggpacket.bytes);
700 5116 : gf_filter_pck_set_cts(dst_pck, st->recomputed_ts);
701 5116 : gf_filter_pck_set_sap(dst_pck, oggpackB_read(&opb, 1) ? GF_FILTER_SAP_NONE : GF_FILTER_SAP_1);
702 5116 : st->recomputed_ts += st->info.frame_rate.num;
703 : }
704 : //this is audio
705 : else {
706 : u32 block_size = 0;
707 :
708 14931 : if (st->info.type==GF_CODECID_VORBIS) {
709 14008 : block_size = gf_vorbis_check_frame(st->vorbis_parser, (char *) oggpacket.packet, oggpacket.bytes);
710 14008 : if (!block_size) continue;
711 : }
712 923 : else if (st->info.type==GF_CODECID_OPUS) {
713 923 : block_size = gf_opus_check_frame(st->opus_parser, (char *) oggpacket.packet, oggpacket.bytes);
714 923 : if (!block_size) continue;
715 :
716 920 : if (!st->recomputed_ts) {
717 : //compat with old arch (keep same hashes), to remove once dropping it
718 3 : if (!gf_sys_old_arch_compat()) {
719 0 : gf_filter_pid_set_property(st->opid, GF_PROP_PID_DELAY, &PROP_LONGSINT( -st->opus_parser->PreSkip));
720 : }
721 : }
722 : }
723 :
724 14924 : if (ogg_page_eos(&oggpage)) {
725 : //compat with old arch (keep same hashes), to remove once dropping it
726 113 : if (!gf_sys_old_arch_compat()) {
727 : /*4.4 End Trimming, cf https://tools.ietf.org/html/rfc7845 */
728 0 : if (oggpacket.granulepos != -1 && granulepos_init != -1)
729 0 : block_size = (u32)(oggpacket.granulepos - granulepos_init - st->recomputed_ts);
730 : }
731 : }
732 14924 : dst_pck = gf_filter_pck_new_alloc(st->opid, oggpacket.bytes, &output);
733 14924 : if (!dst_pck) return GF_OUT_OF_MEM;
734 :
735 14924 : memcpy(output, (char *) oggpacket.packet, oggpacket.bytes);
736 14924 : gf_filter_pck_set_cts(dst_pck, st->recomputed_ts);
737 : //compat with old arch (keep same hashes), to remove once dropping it
738 14924 : if (!gf_sys_old_arch_compat()) {
739 0 : gf_filter_pck_set_duration(dst_pck, block_size);
740 : }
741 :
742 14924 : if (st->info.type == GF_CODECID_VORBIS) {
743 14004 : gf_filter_pck_set_sap(dst_pck, GF_FILTER_SAP_1);
744 920 : } else if (st->info.type == GF_CODECID_OPUS) {
745 : //compat with old arch (keep same hashes), to remove once dropping it
746 920 : if (!gf_sys_old_arch_compat()) {
747 0 : gf_filter_pck_set_roll_info(dst_pck, 3840);
748 0 : gf_filter_pck_set_sap(dst_pck, GF_FILTER_SAP_4);
749 : } else {
750 920 : gf_filter_pck_set_sap(dst_pck, GF_FILTER_SAP_1);
751 : }
752 : } else {
753 0 : gf_filter_pck_set_sap(dst_pck, GF_FILTER_SAP_1);
754 : }
755 :
756 14924 : st->recomputed_ts += block_size;
757 : }
758 :
759 20040 : gf_filter_pck_send(dst_pck);
760 : }
761 : }
762 : }
763 : return GF_OK;
764 : }
765 :
766 11 : static GF_Err oggdmx_initialize(GF_Filter *filter)
767 : {
768 11 : GF_OGGDmxCtx *ctx = gf_filter_get_udta(filter);
769 11 : ctx->streams = gf_list_new();
770 11 : ogg_sync_init(&ctx->oy);
771 11 : return GF_OK;
772 : }
773 :
774 11 : static void oggdmx_finalize(GF_Filter *filter)
775 : {
776 11 : GF_OGGDmxCtx *ctx = gf_filter_get_udta(filter);
777 :
778 : /*just in case something went wrong*/
779 41 : while (gf_list_count(ctx->streams)) {
780 19 : GF_OGGStream *st = (GF_OGGStream*)gf_list_get(ctx->streams, 0);
781 19 : gf_list_rem(ctx->streams, 0);
782 19 : ogg_stream_clear(&st->os);
783 19 : if (st->dsi_bs) gf_bs_del(st->dsi_bs);
784 19 : if (st->vorbis_parser) gf_free(st->vorbis_parser);
785 19 : if (st->opus_parser) gf_free(st->opus_parser);
786 19 : gf_free(st);
787 : }
788 11 : gf_list_del(ctx->streams);
789 11 : ogg_sync_clear(&ctx->oy);
790 11 : }
791 :
792 3074 : static const char *oggdmx_probe_data(const u8 *data, u32 size, GF_FilterProbeScore *score)
793 : {
794 3074 : if (!strncmp(data, "OggS", 4)) {
795 11 : *score = GF_FPROBE_SUPPORTED;
796 11 : return "video/ogg";
797 : }
798 : return NULL;
799 : }
800 :
801 :
802 : static const GF_FilterCapability OGGDmxCaps[] =
803 : {
804 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
805 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "oga|spx|ogg|ogv|oggm|opus"),
806 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "audio/ogg|audio/x-ogg|audio/x-vorbis+ogg|application/ogg|application/x-ogg|video/ogg|video/x-ogg|video/x-ogm+ogg"),
807 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
808 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_VORBIS),
809 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_FLAC),
810 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_OPUS),
811 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_SPEEX),
812 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
813 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_THEORA),
814 : };
815 :
816 : #define OFFS(_n) #_n, offsetof(GF_OGGDmxCtx, _n)
817 : static const GF_FilterArgs OGGDmxArgs[] =
818 : {
819 : { OFFS(index), "indexing window length (unimplemented, only 0 disables stream probing for duration), ", GF_PROP_DOUBLE, "1.0", NULL, 0},
820 : {0}
821 : };
822 :
823 :
824 : GF_FilterRegister OGGDmxRegister = {
825 : .name = "oggdmx",
826 : GF_FS_SET_DESCRIPTION("OGG demuxer")
827 : GF_FS_SET_HELP("This filter demultiplexes OGG files/data into a set of media PIDs and frames.")
828 : .private_size = sizeof(GF_OGGDmxCtx),
829 : .initialize = oggdmx_initialize,
830 : .finalize = oggdmx_finalize,
831 : .args = OGGDmxArgs,
832 : .flags = GF_FS_REG_DYNAMIC_PIDS,
833 : SETCAPS(OGGDmxCaps),
834 : .configure_pid = oggdmx_configure_pid,
835 : .process = oggdmx_process,
836 : .process_event = oggdmx_process_event,
837 : .probe_data = oggdmx_probe_data,
838 : };
839 :
840 : #endif // !defined(GPAC_DISABLE_AV_PARSERS) && !defined(GPAC_DISABLE_OGG)
841 :
842 2877 : const GF_FilterRegister *oggdmx_register(GF_FilterSession *session)
843 : {
844 : #if !defined(GPAC_DISABLE_AV_PARSERS) && !defined(GPAC_DISABLE_OGG)
845 2877 : return &OGGDmxRegister;
846 : #else
847 : return NULL;
848 : #endif
849 :
850 : }
851 :
|