LCOV - code coverage report
Current view: top level - filters - dmx_ogg.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 322 400 80.5 %
Date: 2021-04-29 23:48:07 Functions: 13 13 100.0 %

          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             : 

Generated by: LCOV version 1.13