LCOV - code coverage report
Current view: top level - filters - reframe_mp3.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 338 390 86.7 %
Date: 2021-04-29 23:48:07 Functions: 12 12 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 / MP3 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             : #ifndef GPAC_DISABLE_AV_PARSERS
      31             : 
      32             : typedef struct
      33             : {
      34             :         u64 pos;
      35             :         Double duration;
      36             : } MP3Idx;
      37             : 
      38             : typedef struct
      39             : {
      40             :         //filter args
      41             :         Double index;
      42             :         Bool expart;
      43             : 
      44             :         //only one input pid declared
      45             :         GF_FilterPid *ipid;
      46             :         //only one output pid declared
      47             :         GF_FilterPid *opid;
      48             : 
      49             :         GF_BitStream *bs;
      50             :         u64 file_pos, cts, prev_cts;
      51             :         u32 sr, nb_ch, codecid;
      52             :         GF_Fraction64 duration;
      53             :         Double start_range;
      54             :         Bool in_seek;
      55             :         u32 timescale;
      56             :         Bool is_playing;
      57             :         Bool is_file;
      58             :         Bool initial_play_done, file_loaded;
      59             : 
      60             :         u32 hdr;
      61             : 
      62             :         u8 *mp3_buffer;
      63             :         u32 mp3_buffer_size, mp3_buffer_alloc, resume_from;
      64             :         u64 byte_offset;
      65             : 
      66             :         GF_FilterPacket *src_pck;
      67             : 
      68             :         Bool recompute_cts;
      69             :         MP3Idx *indexes;
      70             :         u32 index_alloc_size, index_size;
      71             : 
      72             :         u32 tag_size;
      73             :         u8 *id3_buffer;
      74             :         u32 id3_buffer_size, id3_buffer_alloc;
      75             : 
      76             :         GF_FilterPid *vpid;
      77             : } GF_MP3DmxCtx;
      78             : 
      79             : 
      80             : 
      81             : 
      82          72 : GF_Err mp3_dmx_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
      83             : {
      84             :         const GF_PropertyValue *p;
      85          72 :         GF_MP3DmxCtx *ctx = gf_filter_get_udta(filter);
      86             : 
      87          72 :         if (is_remove) {
      88           0 :                 ctx->ipid = NULL;
      89           0 :                 if (ctx->opid) {
      90           0 :                         gf_filter_pid_remove(ctx->opid);
      91           0 :                         ctx->opid = NULL;
      92             :                 }
      93             :                 return GF_OK;
      94             :         }
      95          72 :         if (! gf_filter_pid_check_caps(pid))
      96             :                 return GF_NOT_SUPPORTED;
      97             : 
      98          72 :         ctx->ipid = pid;
      99          72 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_TIMESCALE);
     100          72 :         if (p) ctx->timescale = p->value.uint;
     101             : 
     102          72 :         p = gf_filter_pid_get_property_str(pid, "nocts");
     103          72 :         if (p && p->value.boolean) ctx->recompute_cts = GF_TRUE;
     104          69 :         else ctx->recompute_cts = GF_FALSE;
     105             : 
     106          72 :         if (ctx->timescale && !ctx->opid) {
     107          19 :                 ctx->opid = gf_filter_pid_new(filter);
     108          19 :                 gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
     109          19 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_UNFRAMED, NULL);
     110             :         }
     111             :         return GF_OK;
     112             : }
     113             : 
     114       13960 : static void mp3_dmx_check_dur(GF_Filter *filter, GF_MP3DmxCtx *ctx)
     115             : {
     116             :         FILE *stream;
     117             :         u64 duration, cur_dur;
     118             :         s32 prev_sr = -1;
     119             :         const GF_PropertyValue *p;
     120       13960 :         if (!ctx->opid || ctx->timescale || ctx->file_loaded) return;
     121             : 
     122          51 :         if (ctx->index<=0) {
     123          25 :                 ctx->file_loaded = GF_TRUE;
     124             :                 return;
     125             :         }
     126             : 
     127          26 :         p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILEPATH);
     128          26 :         if (!p || !p->value.string || !strncmp(p->value.string, "gmem://", 7)) {
     129           1 :                 ctx->is_file = GF_FALSE;
     130           1 :                 ctx->file_loaded = GF_TRUE;
     131             :                 return;
     132             :         }
     133          25 :         ctx->is_file = GF_TRUE;
     134             : 
     135          25 :         stream = gf_fopen(p->value.string, "rb");
     136          25 :         if (!stream) return;
     137             : 
     138          25 :         ctx->index_size = 0;
     139             : 
     140             :         duration = 0;
     141             :         cur_dur = 0;
     142             :         while (1) {
     143             :                 u32 sr, dur;
     144             :                 u64 pos;
     145        9150 :                 u32 hdr = gf_mp3_get_next_header(stream);
     146        9150 :                 if (!hdr) break;
     147        9125 :                 sr = gf_mp3_sampling_rate(hdr);
     148             : 
     149        9125 :                 if ((prev_sr>=0) && (prev_sr != sr)) {
     150           1 :                         duration *= sr;
     151           1 :                         duration /= prev_sr;
     152             : 
     153           1 :                         cur_dur *= sr;
     154           1 :                         cur_dur /= prev_sr;
     155             :                 }
     156        9125 :                 prev_sr = sr;
     157        9125 :                 dur = gf_mp3_window_size(hdr);
     158        9125 :                 duration += dur;
     159        9125 :                 cur_dur += dur;
     160        9125 :                 pos = gf_ftell(stream);
     161        9125 :                 if (cur_dur > ctx->index * prev_sr) {
     162         213 :                         if (!ctx->index_alloc_size) ctx->index_alloc_size = 10;
     163         189 :                         else if (ctx->index_alloc_size == ctx->index_size) ctx->index_alloc_size *= 2;
     164         213 :                         ctx->indexes = gf_realloc(ctx->indexes, sizeof(MP3Idx)*ctx->index_alloc_size);
     165         213 :                         ctx->indexes[ctx->index_size].pos = pos - 4;
     166         213 :                         ctx->indexes[ctx->index_size].duration = (Double) duration;
     167         213 :                         ctx->indexes[ctx->index_size].duration /= prev_sr;
     168         213 :                         ctx->index_size ++;
     169             :                         cur_dur = 0;
     170             :                 }
     171             : 
     172        9125 :                 pos = gf_ftell(stream);
     173        9125 :                 gf_fseek(stream, pos + gf_mp3_frame_size(hdr) - 4, SEEK_SET);
     174             :         }
     175          25 :         gf_fclose(stream);
     176             : 
     177          25 :         if (!ctx->duration.num || (ctx->duration.num  * prev_sr != duration * ctx->duration.den)) {
     178          25 :                 ctx->duration.num = (s32) duration;
     179          25 :                 ctx->duration.den = prev_sr ;
     180             : 
     181          25 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, & PROP_FRAC64(ctx->duration));
     182             :         }
     183             : 
     184          25 :         p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILE_CACHED);
     185          25 :         if (p && p->value.boolean) ctx->file_loaded = GF_TRUE;
     186          25 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CAN_DATAREF, & PROP_BOOL(GF_TRUE ) );
     187             : }
     188             : 
     189             : 
     190             : #include <gpac/utf.h>
     191           4 : static void id3dmx_set_string(GF_FilterPid *apid, char *name, u8 *buf, Bool is_dyn)
     192             : {
     193           4 :         if ((buf[0]==0xFF) || (buf[0]==0xFE)) {
     194           3 :                 const u16 *sptr = (u16 *) (buf+2);
     195           3 :                 s32 len = (s32) ( UTF8_MAX_BYTES_PER_CHAR * gf_utf8_wcslen(sptr) );
     196           3 :                 char *tmp = gf_malloc(len+1);
     197           3 :                 len = (s32) gf_utf8_wcstombs(tmp, len, &sptr);
     198           3 :                 if (len>=0) {
     199           3 :                         tmp[len] = 0;
     200           3 :                         if (is_dyn) {
     201           0 :                                 gf_filter_pid_set_property_dyn(apid, name, &PROP_STRING(tmp) );
     202             :                         } else {
     203           3 :                                 gf_filter_pid_set_property_str(apid, name, &PROP_STRING(tmp) );
     204             :                         }
     205             :                 } else {
     206           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[MP3Dmx] Corrupted ID3 text frame %s\n", name));
     207             :                 }
     208           3 :                 gf_free(tmp);
     209             :         } else {
     210           1 :                 if (is_dyn) {
     211           0 :                         gf_filter_pid_set_property_dyn(apid, name, &PROP_STRING(buf) );
     212             :                 } else {
     213           1 :                         gf_filter_pid_set_property_str(apid, name, &PROP_STRING(buf) );
     214             :                 }
     215             :         }
     216           4 : }
     217             : 
     218           1 : void id3dmx_flush(GF_Filter *filter, u8 *id3_buf, u32 id3_buf_size, GF_FilterPid *audio_pid, GF_FilterPid **video_pid_p)
     219             : {
     220           1 :         GF_BitStream *bs = gf_bs_new(id3_buf, id3_buf_size, GF_BITSTREAM_READ);
     221             :         char *sep_desc;
     222             :         char *_buf=NULL;
     223             :         u32 buf_alloc=0;
     224           1 :         gf_bs_skip_bytes(bs, 3);
     225           1 :         /*u8 major = */gf_bs_read_u8(bs);
     226           1 :         /*u8 minor = */gf_bs_read_u8(bs);
     227           1 :         /*u8 unsync = */gf_bs_read_int(bs, 1);
     228           1 :         u8 ext_hdr = gf_bs_read_int(bs, 1);
     229           1 :         gf_bs_read_int(bs, 6);
     230           1 :         u32 size = gf_id3_read_size(bs);
     231             : 
     232             : 
     233             :         if (ext_hdr) {
     234             : 
     235             :         }
     236             : 
     237           1 :         while (size && (gf_bs_available(bs)>=10) ) {
     238             :                 char *buf;
     239             :                 char szTag[1024];
     240             :                 char *sep;
     241             :                 s32 tag_idx;
     242             :                 u32 pic_size;
     243             :                 //u32 pic_type;
     244           5 :                 u32 ftag = gf_bs_read_u32(bs);
     245           5 :                 u32 fsize = gf_id3_read_size(bs);
     246           5 :                 /*u16 fflags = */gf_bs_read_u16(bs);
     247             : 
     248           5 :                 size -= 10;
     249           5 :                 if (!fsize)
     250             :                         break;
     251             : 
     252           4 :                 if (size<fsize) {
     253           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[MP3Dmx] Broken ID3 frame tag %s, size %d but remaining bytes %d\n", gf_4cc_to_str(ftag), fsize, size));
     254             :                         break;
     255             :                 }
     256             : 
     257           4 :                 if (buf_alloc<=fsize) {
     258           2 :                         _buf = gf_realloc(_buf, fsize+3);
     259             :                         buf_alloc = fsize+3;
     260             :                 }
     261             :                 //read into _buf+1 so that buf+1 is always %2 mem aligned as it can be loaded as unsigned short
     262           4 :                 gf_bs_read_data(bs, _buf+1, fsize);
     263           4 :                 _buf[fsize+1]=0;
     264           4 :                 _buf[fsize+2]=0;
     265             :                 buf = _buf+1;
     266             : 
     267           4 :                 tag_idx = gf_itags_find_by_id3tag(ftag);
     268           4 :                 if (tag_idx>=0) {
     269           4 :                         const char *tag_name = gf_itags_get_name((u32) tag_idx);
     270           4 :                         id3dmx_set_string(audio_pid, (char *) tag_name, buf+1, GF_FALSE);
     271           0 :                 } else if (ftag==GF_ID3V2_FRAME_TXXX) {
     272           0 :                         sep = memchr(buf, 0, fsize);
     273           0 :                         if (sep) {
     274           0 :                                 if (!stricmp(buf+1, "comment")) {
     275           0 :                                         id3dmx_set_string(audio_pid, "comment", sep+1, GF_FALSE);
     276             :                                 } else {
     277             :                                         strcpy(szTag, "tag_");
     278             :                                         strncat(szTag, buf+1, 1019);
     279           0 :                                         id3dmx_set_string(audio_pid, szTag, sep+1, GF_TRUE);
     280             :                                 }
     281             :                         }
     282           0 :                 } else if (ftag == GF_ID3V2_FRAME_APIC) {
     283             :                         //first char is text encoding
     284             :                         //then mime
     285           0 :                         sep = memchr(buf+1, 0, fsize-1);
     286             :                         /*pic_type = sep[1];*/
     287           0 :                         sep_desc = memchr(sep+2, 0, fsize-1);
     288             : 
     289           0 :                         if (sep_desc) {
     290             :                                 GF_Err e;
     291           0 :                                 pic_size = (u32) ( (sep_desc + 1) - buf);
     292           0 :                                 pic_size = fsize - pic_size;
     293             : 
     294           0 :                                 if (video_pid_p) {
     295           0 :                                         e = gf_filter_pid_raw_new(filter, NULL, NULL, buf+1, NULL, sep_desc+1, pic_size, GF_FALSE, video_pid_p);
     296           0 :                                         if (e) {
     297           0 :                                                 GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[MP3Dmx] error setting up video pid for cover art: %s\n", gf_error_to_string(e) ));
     298             :                                         }
     299           0 :                                         if (*video_pid_p) {
     300             :                                                 u8 *out_buffer;
     301             :                                                 GF_FilterPacket *dst_pck;
     302           0 :                                                 gf_filter_pid_set_name(*video_pid_p, "CoverArt");
     303           0 :                                                 gf_filter_pid_set_property(*video_pid_p, GF_PROP_PID_COVER_ART, &PROP_BOOL(GF_TRUE));
     304           0 :                                                 dst_pck = gf_filter_pck_new_alloc(*video_pid_p, pic_size, &out_buffer);
     305           0 :                                                 if (dst_pck) {
     306           0 :                                                         gf_filter_pck_set_framing(dst_pck, GF_TRUE, GF_TRUE);
     307           0 :                                                         memcpy(out_buffer, sep_desc+1, pic_size);
     308           0 :                                                         gf_filter_pck_send(dst_pck);
     309             :                                                 }
     310             : 
     311           0 :                                                 gf_filter_pid_set_eos(*video_pid_p);
     312             :                                         }
     313             :                                 } else {
     314           0 :                                         gf_filter_pid_set_property(audio_pid, GF_PROP_PID_COVER_ART, &PROP_DATA(sep_desc+1, pic_size) );
     315             :                                 }
     316             :                         }
     317             :                 } else {
     318           0 :                         sprintf(szTag, "tag_%s", gf_4cc_to_str(ftag));
     319           0 :                         if ((ftag>>24) == 'T') {
     320           0 :                                 id3dmx_set_string(audio_pid, szTag, buf+1, GF_TRUE);
     321             :                         } else {
     322           0 :                                 gf_filter_pid_set_property_dyn(audio_pid, szTag, &PROP_DATA(buf, fsize) );
     323             :                         }
     324             :                         break;
     325             :                 }
     326           4 :                 size -= fsize;
     327             :         }
     328           1 :         gf_bs_del(bs);
     329           1 :         if (_buf) gf_free(_buf);
     330           1 : }
     331           1 : static void mp3_dmx_flush_id3(GF_Filter *filter, GF_MP3DmxCtx *ctx)
     332             : {
     333           1 :         id3dmx_flush(filter, ctx->id3_buffer, ctx->id3_buffer_size, ctx->opid, ctx->expart ? &ctx->vpid : NULL);
     334           1 :         ctx->id3_buffer_size = 0;
     335           1 : }
     336             : 
     337       23361 : static void mp3_dmx_check_pid(GF_Filter *filter, GF_MP3DmxCtx *ctx)
     338             : {
     339             :         u32 sr;
     340             : 
     341       23361 :         if (!ctx->opid) {
     342          51 :                 ctx->opid = gf_filter_pid_new(filter);
     343          51 :                 mp3_dmx_check_dur(filter, ctx);
     344             :         }
     345             : 
     346       23361 :         if ((ctx->sr == gf_mp3_sampling_rate(ctx->hdr)) && (ctx->nb_ch == gf_mp3_num_channels(ctx->hdr) )
     347       23291 :                 && (ctx->codecid == gf_mp3_object_type_indication(ctx->hdr) )
     348             :         )
     349             :                 return;
     350             : 
     351             :         //copy properties at init or reconfig
     352          70 :         gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
     353          70 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, & PROP_UINT( GF_STREAM_AUDIO));
     354          70 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_UNFRAMED, NULL );
     355          70 :         if (ctx->is_file && ctx->index) {
     356          25 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PLAYBACK_MODE, & PROP_UINT(GF_PLAYBACK_MODE_FASTFORWARD) );
     357             :         }
     358          70 :         if (ctx->duration.num)
     359          25 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, & PROP_FRAC64(ctx->duration));
     360             : 
     361          70 :         if (!ctx->timescale) gf_filter_pid_set_name(ctx->opid, "audio");
     362             : 
     363          70 :         ctx->nb_ch = gf_mp3_num_channels(ctx->hdr);
     364          70 :         ctx->codecid = gf_mp3_object_type_indication(ctx->hdr);
     365          70 :         sr = gf_mp3_sampling_rate(ctx->hdr);
     366             : 
     367          70 :         if (!ctx->timescale) {
     368             :                 //we change sample rate, change cts
     369          51 :                 if (ctx->cts && ctx->sr && (ctx->sr != sr)) {
     370           0 :                         ctx->cts *= sr;
     371           0 :                         ctx->cts /= ctx->sr;
     372             :                 }
     373             :         }
     374          70 :         ctx->sr = sr;
     375             : 
     376          70 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_TIMESCALE, & PROP_UINT(ctx->timescale ? ctx->timescale : sr));
     377          70 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLE_RATE, & PROP_UINT(sr));
     378          70 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_NUM_CHANNELS, & PROP_UINT(ctx->nb_ch) );
     379          70 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, & PROP_UINT(gf_mp3_object_type_indication(ctx->hdr) ) );
     380          70 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLES_PER_FRAME, & PROP_UINT(gf_mp3_window_size(ctx->hdr) ) );
     381             : 
     382          70 :         if (!gf_sys_is_test_mode() ) {
     383           0 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_BITRATE, & PROP_UINT(gf_mp3_bit_rate(ctx->hdr) ) );
     384             :         }
     385             : 
     386          70 :         if (ctx->id3_buffer_size)
     387           1 :                 mp3_dmx_flush_id3(filter, ctx);
     388             : 
     389             : }
     390             : 
     391        1542 : static Bool mp3_dmx_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
     392             : {
     393             :         u32 i;
     394             :         GF_FilterEvent fevt;
     395        1542 :         GF_MP3DmxCtx *ctx = gf_filter_get_udta(filter);
     396             : 
     397        1542 :         if (evt->base.on_pid != ctx->opid) return GF_TRUE;
     398             : 
     399         112 :         switch (evt->base.type) {
     400          77 :         case GF_FEVT_PLAY:
     401          77 :                 if (!ctx->is_playing) {
     402          77 :                         ctx->is_playing = GF_TRUE;
     403             :                 }
     404          77 :                 if (! ctx->is_file) {
     405          45 :                         if (evt->play.start_range || ctx->initial_play_done) {
     406           0 :                                 ctx->mp3_buffer_size = 0;
     407           0 :                                 ctx->resume_from = 0;
     408             :                         }
     409          45 :                         ctx->initial_play_done = GF_TRUE;
     410          45 :                         return GF_FALSE;
     411             :                 }
     412          32 :                 mp3_dmx_check_dur(filter, ctx);
     413             : 
     414          32 :                 ctx->start_range = evt->play.start_range;
     415          32 :                 ctx->in_seek = GF_TRUE;
     416          32 :                 ctx->file_pos = 0;
     417          32 :                 if (ctx->start_range) {
     418           1 :                         for (i=1; i<ctx->index_size; i++) {
     419           2 :                                 if (ctx->indexes[i].duration>ctx->start_range) {
     420           1 :                                         ctx->cts = (u64) (ctx->indexes[i-1].duration * ctx->sr);
     421           1 :                                         ctx->file_pos = ctx->indexes[i-1].pos;
     422           1 :                                         break;
     423             :                                 }
     424             :                         }
     425             :                 }
     426          32 :                 if (!ctx->initial_play_done) {
     427          25 :                         ctx->initial_play_done = GF_TRUE;
     428             :                         //seek will not change the current source state, don't send a seek
     429          25 :                         if (!ctx->file_pos)
     430             :                                 return GF_TRUE;
     431             :                 }
     432           7 :                 ctx->mp3_buffer_size = 0;
     433           7 :                 ctx->resume_from = 0;
     434             :                 //post a seek
     435           7 :                 GF_FEVT_INIT(fevt, GF_FEVT_SOURCE_SEEK, ctx->ipid);
     436           7 :                 fevt.seek.start_offset = ctx->file_pos;
     437           7 :                 gf_filter_pid_send_event(ctx->ipid, &fevt);
     438             : 
     439             :                 //cancel event
     440           7 :                 return GF_TRUE;
     441             : 
     442          33 :         case GF_FEVT_STOP:
     443          33 :                 ctx->is_playing = GF_FALSE;
     444          33 :                 if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
     445          33 :                 ctx->src_pck = NULL;
     446             :                 //don't cancel event
     447          33 :                 return GF_FALSE;
     448             : 
     449             :         case GF_FEVT_SET_SPEED:
     450             :                 //cancel event
     451             :                 return GF_TRUE;
     452             :         default:
     453             :                 break;
     454             :         }
     455             :         //by default don't cancel event - to rework once we have downloading in place
     456           0 :         return GF_FALSE;
     457             : }
     458             : 
     459       23310 : static GFINLINE void mp3_dmx_update_cts(GF_MP3DmxCtx *ctx)
     460             : {
     461       23310 :         u32 nb_samp = gf_mp3_window_size(ctx->hdr);
     462       23310 :         if (ctx->timescale) {
     463        9161 :                 u64 inc = nb_samp;
     464        9161 :                 inc *= ctx->timescale;
     465        9161 :                 inc /= ctx->sr;
     466        9161 :                 ctx->cts += inc;
     467             :         } else {
     468       14149 :                 ctx->cts += nb_samp;
     469             :         }
     470       23310 : }
     471             : 
     472       14388 : GF_Err mp3_dmx_process(GF_Filter *filter)
     473             : {
     474       14388 :         GF_MP3DmxCtx *ctx = gf_filter_get_udta(filter);
     475             :         GF_FilterPacket *pck, *dst_pck;
     476             :         u8 *data, *output;
     477             :         u8 *start;
     478             :         u32 pck_size, remain, prev_pck_size;
     479             :         u64 cts = GF_FILTER_NO_TS;
     480             : 
     481             :         //always reparse duration
     482       14388 :         if (!ctx->duration.num)
     483       13877 :                 mp3_dmx_check_dur(filter, ctx);
     484             : 
     485       14388 :         if (ctx->opid && !ctx->is_playing)
     486             :                 return GF_OK;
     487             : 
     488       14245 :         pck = gf_filter_pid_get_packet(ctx->ipid);
     489       14245 :         if (!pck) {
     490        9479 :                 if (gf_filter_pid_is_eos(ctx->ipid)) {
     491         107 :                         if (!ctx->mp3_buffer_size) {
     492          57 :                                 if (ctx->opid)
     493          57 :                                         gf_filter_pid_set_eos(ctx->opid);
     494          57 :                                 if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
     495          57 :                                 ctx->src_pck = NULL;
     496          57 :                                 return GF_EOS;
     497             :                         }
     498             :                 } else {
     499             :                         return GF_OK;
     500             :                 }
     501             :         }
     502             : 
     503        4816 :         prev_pck_size = ctx->mp3_buffer_size;
     504        4816 :         if (pck && !ctx->resume_from) {
     505        4715 :                 data = (char *) gf_filter_pck_get_data(pck, &pck_size);
     506             : 
     507        4715 :                 if (ctx->byte_offset != GF_FILTER_NO_BO) {
     508         572 :                         u64 byte_offset = gf_filter_pck_get_byte_offset(pck);
     509         572 :                         if (!ctx->mp3_buffer_size) {
     510          75 :                                 ctx->byte_offset = byte_offset;
     511         497 :                         } else if (ctx->byte_offset + ctx->mp3_buffer_size != byte_offset) {
     512           1 :                                 ctx->byte_offset = GF_FILTER_NO_BO;
     513           1 :                                 if ((byte_offset != GF_FILTER_NO_BO) && (byte_offset>ctx->mp3_buffer_size) ) {
     514           1 :                                         ctx->byte_offset = byte_offset - ctx->mp3_buffer_size;
     515             :                                 }
     516             :                         }
     517             :                 }
     518             : 
     519        4715 :                 if (ctx->mp3_buffer_size + pck_size > ctx->mp3_buffer_alloc) {
     520         352 :                         ctx->mp3_buffer_alloc = ctx->mp3_buffer_size + pck_size;
     521         352 :                         ctx->mp3_buffer = gf_realloc(ctx->mp3_buffer, ctx->mp3_buffer_alloc);
     522             :                 }
     523        4715 :                 memcpy(ctx->mp3_buffer + ctx->mp3_buffer_size, data, pck_size);
     524        4715 :                 ctx->mp3_buffer_size += pck_size;
     525             :         }
     526             : 
     527             :         //input pid sets some timescale - we flushed pending data , update cts
     528        4816 :         if (ctx->timescale && pck) {
     529        4162 :                 cts = gf_filter_pck_get_cts(pck);
     530             :         }
     531             : 
     532        4162 :         if (cts == GF_FILTER_NO_TS) {
     533             :                 //avoids updating cts
     534             :                 prev_pck_size = 0;
     535             :         }
     536             : 
     537        4816 :         remain = ctx->mp3_buffer_size;
     538        4816 :         start = ctx->mp3_buffer;
     539             : 
     540        4816 :         if (ctx->resume_from) {
     541          51 :                 start += ctx->resume_from - 1;
     542          51 :                 remain -= ctx->resume_from - 1;
     543          51 :                 ctx->resume_from = 0;
     544             :         }
     545             : 
     546       28129 :         while (remain) {
     547             :                 u8 *sync;
     548             :                 Bool skip_id3v1=GF_FALSE;
     549       28079 :                 u32 bytes_skipped=0, size, nb_samp, bytes_to_drop=0;;
     550             : 
     551       28079 :                 if (!ctx->tag_size && (remain>3)) {
     552             : 
     553             :                         /* Did we read an ID3v2 ? */
     554       28069 :                         if (start[0] == 'I' && start[1] == 'D' && start[2] == '3') {
     555           1 :                                 if (remain<10)
     556          51 :                                         return GF_OK;
     557             : 
     558           1 :                                 ctx->tag_size = ((start[9] & 0x7f) + ((start[8] & 0x7f) << 7) + ((start[7] & 0x7f) << 14) + ((start[6] & 0x7f) << 21));
     559             : 
     560             :                                 bytes_to_drop = 10;
     561           1 :                                 if (ctx->id3_buffer_alloc < ctx->tag_size+10) {
     562           1 :                                         ctx->id3_buffer = gf_realloc(ctx->id3_buffer, ctx->tag_size+10);
     563           1 :                                         ctx->id3_buffer_alloc = ctx->tag_size+10;
     564             :                                 }
     565           1 :                                 memcpy(ctx->id3_buffer, start, 10);
     566           1 :                                 ctx->id3_buffer_size = 10;
     567             :                                 goto drop_byte;
     568             :                         }
     569             :                 }
     570       28078 :                 if (ctx->tag_size) {
     571           1 :                         if (ctx->tag_size>remain) {
     572             :                                 bytes_to_drop = remain;
     573           0 :                                 ctx->tag_size-=remain;
     574             :                         } else {
     575             :                                 bytes_to_drop = ctx->tag_size;
     576           1 :                                 ctx->tag_size = 0;
     577             :                         }
     578           1 :                         memcpy(ctx->id3_buffer + ctx->id3_buffer_size, start, bytes_to_drop);
     579           1 :                         ctx->id3_buffer_size += bytes_to_drop;
     580             : 
     581           1 :                         if (!ctx->tag_size && ctx->opid) {
     582           0 :                                 mp3_dmx_flush_id3(filter, ctx);
     583             :                         }
     584             :                         goto drop_byte;
     585             : 
     586             :                 }
     587             : 
     588       28077 :                 ctx->hdr = gf_mp3_get_next_header_mem(start, remain, &bytes_skipped);
     589             : 
     590             :                 //couldn't find sync byte in this packet
     591       28077 :                 if (!ctx->hdr) {
     592             :                         break;
     593             :                 }
     594       28068 :                 sync = start + bytes_skipped;
     595             : 
     596       28068 :                 size = gf_mp3_frame_size(ctx->hdr);
     597             : 
     598             : 
     599             :                 //ready to send packet
     600       28068 :                 if (size + 1 < remain-bytes_skipped) {
     601             :                         //make sure we are sync!
     602       23312 :                         if (sync[size] !=0xFF) {
     603           1 :                                 if ((sync[size]=='T') && (sync[size+1]=='A') && (sync[size+2]=='G')) {
     604             :                                         skip_id3v1=GF_TRUE;
     605             :                                 } else {
     606           1 :                                         GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[MP3Dmx] invalid frame, resyncing\n"));
     607             :                                         goto drop_byte;
     608             :                                 }
     609             :                         }
     610             :                 }
     611             :                 //otherwise wait for next frame, unless if end of stream
     612        4756 :                 else if (pck) {
     613             :                         break;
     614             :                 }
     615             :                 //ready to send packet
     616       23361 :                 mp3_dmx_check_pid(filter, ctx);
     617             : 
     618       23361 :                 if (!ctx->is_playing) {
     619          51 :                         ctx->resume_from = (u32) (sync - ctx->mp3_buffer + 1);
     620          51 :                         return GF_OK;
     621             :                 }
     622             : 
     623       23310 :                 nb_samp = gf_mp3_window_size(ctx->hdr);
     624             : 
     625       23310 :                 if (ctx->in_seek) {
     626          61 :                         u64 nb_samples_at_seek = (u64) (ctx->start_range * ctx->sr);
     627          61 :                         if (ctx->cts + nb_samp >= nb_samples_at_seek) {
     628             :                                 //u32 samples_to_discard = (ctx->cts + nb_samp ) - nb_samples_at_seek;
     629          30 :                                 ctx->in_seek = GF_FALSE;
     630             :                         }
     631             :                 }
     632             : 
     633       23310 :                 bytes_to_drop = bytes_skipped + size;
     634       23310 :                 if (ctx->timescale && !prev_pck_size && (cts != GF_FILTER_NO_TS) ) {
     635        1038 :                         ctx->cts = cts;
     636             :                         cts = GF_FILTER_NO_TS;
     637             :                 }
     638             : 
     639       23310 :                 if (!ctx->in_seek) {
     640       23279 :                         dst_pck = gf_filter_pck_new_alloc(ctx->opid, size, &output);
     641       23279 :                         if (!dst_pck) break;
     642       23279 :                         memcpy(output, sync, size);
     643             : 
     644       23279 :                         gf_filter_pck_set_cts(dst_pck, ctx->cts);
     645       23279 :                         gf_filter_pck_set_duration(dst_pck, nb_samp);
     646       23279 :                         gf_filter_pck_set_sap(dst_pck, GF_FILTER_SAP_1);
     647       23279 :                         gf_filter_pck_set_framing(dst_pck, GF_TRUE, GF_TRUE);
     648             : 
     649       23279 :                         if (ctx->byte_offset != GF_FILTER_NO_BO) {
     650       14118 :                                 gf_filter_pck_set_byte_offset(dst_pck, ctx->byte_offset + bytes_skipped);
     651             :                         }
     652             : 
     653       23279 :                         gf_filter_pck_send(dst_pck);
     654             :                 }
     655       23310 :                 mp3_dmx_update_cts(ctx);
     656             : 
     657             :                 //TODO, parse id3v1 ??
     658       23310 :                 if (skip_id3v1)
     659           0 :                         bytes_to_drop+=128;
     660             : 
     661             :                 //truncated last frame
     662       23310 :                 if (bytes_to_drop>remain) {
     663          16 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[MP3Dmx] truncated frame!\n"));
     664             :                         bytes_to_drop=remain;
     665             :                 }
     666             : 
     667       46605 : drop_byte:
     668       23311 :                 if (!bytes_to_drop) {
     669             :                         bytes_to_drop = 1;
     670             :                 }
     671       23313 :                 start += bytes_to_drop;
     672       23313 :                 remain -= bytes_to_drop;
     673             : 
     674       23313 :                 if (prev_pck_size) {
     675        4143 :                         if (prev_pck_size > bytes_to_drop) prev_pck_size -= bytes_to_drop;
     676             :                         else {
     677             :                                 prev_pck_size=0;
     678        4143 :                                 if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
     679        4143 :                                 ctx->src_pck = pck;
     680        4143 :                                 if (pck)
     681        4143 :                                         gf_filter_pck_ref_props(&ctx->src_pck);
     682             :                         }
     683             :                 }
     684       23313 :                 if (ctx->byte_offset != GF_FILTER_NO_BO)
     685       14152 :                         ctx->byte_offset += bytes_to_drop;
     686             :         }
     687             : 
     688        4765 :         if (!pck) {
     689          50 :                 ctx->mp3_buffer_size = 0;
     690          50 :                 return mp3_dmx_process(filter);
     691             :         } else {
     692        4715 :                 if (remain) {
     693        4715 :                         memmove(ctx->mp3_buffer, start, remain);
     694             :                 }
     695        4715 :                 ctx->mp3_buffer_size = remain;
     696        4715 :                 gf_filter_pid_drop_packet(ctx->ipid);
     697             :         }
     698        4715 :         return GF_OK;
     699             : }
     700             : 
     701          70 : static void mp3_dmx_finalize(GF_Filter *filter)
     702             : {
     703          70 :         GF_MP3DmxCtx *ctx = gf_filter_get_udta(filter);
     704          70 :         if (ctx->bs) gf_bs_del(ctx->bs);
     705          70 :         if (ctx->indexes) gf_free(ctx->indexes);
     706          70 :         if (ctx->mp3_buffer) gf_free(ctx->mp3_buffer);
     707          70 :         if (ctx->id3_buffer) gf_free(ctx->id3_buffer);
     708          70 :         if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
     709          70 : }
     710             : 
     711             : 
     712        3074 : static const char *mp3_dmx_probe_data(const u8 *data, u32 size, GF_FilterProbeScore *score)
     713             : {
     714             :         u32 nb_frames=0;
     715        3074 :         u32 pos=0;
     716             :         u32 prev_pos=0;
     717             :         s32 prev_sr_idx=-1;
     718             :         s32 prev_ch=-1;
     719             :         s32 prev_layer=-1;
     720             :         s32 init_pos = -1;
     721             :         Bool has_id3 = GF_FALSE;
     722             : 
     723             :         /* Check for ID3 */
     724        3074 :         if (size>= 10) {
     725        3070 :                 if (data[0] == 'I' && data[1] == 'D' && data[2] == '3') {
     726           1 :                         u32 tag_size = ((data[9] & 0x7f) + ((data[8] & 0x7f) << 7) + ((data[7] & 0x7f) << 14) + ((data[6] & 0x7f) << 21));
     727             : 
     728           1 :                         if (tag_size+10>size) {
     729           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("ID3 tag detected size %d but probe data only %d bytes, will rely on file extension (try increasing probe size using --block_size)\n", tag_size+10, size));
     730           0 :                                 *score = GF_FPROBE_EXT_MATCH;
     731           0 :                                 return "mp3|mp2|mp1";
     732             :                         }
     733           1 :                         data += tag_size+10;
     734           1 :                         size -= tag_size+10;
     735             :                         has_id3 = GF_TRUE;
     736             :                 }
     737             :         }
     738             : 
     739        1413 :         while (1) {
     740        4487 :                 u32 hdr = gf_mp3_get_next_header_mem(data, size, &pos);
     741        4487 :                 if (!hdr) break;
     742             : 
     743        2299 :                 if (init_pos<0) init_pos = pos;
     744             : 
     745        2299 :                 if (gf_mp3_version(hdr) > 3)
     746             :                         break;
     747             :                 //check sample rate
     748        2299 :                 u8 val = (hdr >> 10) & 0x3;
     749        2299 :                 if (val>2)
     750             :                         break;
     751        2299 :                 u32 fsize = gf_mp3_frame_size(hdr);
     752        2299 :                 if (prev_pos && pos) {
     753             :                         nb_frames=0;
     754             :                         break;
     755             :                 }
     756             : 
     757        1485 :                 if (prev_sr_idx>=0) {
     758         252 :                         if ((u8) prev_sr_idx != val) {
     759             :                                 nb_frames=0;
     760             :                                 break;
     761             :                         }
     762             :                 }
     763        1485 :                 prev_sr_idx = val;
     764             : 
     765        1485 :                 val = gf_mp3_num_channels(hdr);
     766        1485 :                 if (prev_ch>=0) {
     767         252 :                         if ((u8) prev_ch != val) {
     768             :                                 nb_frames=0;
     769             :                                 break;
     770             :                         }
     771             :                 }
     772        1485 :                 prev_ch = val;
     773             : 
     774        1485 :                 val = gf_mp3_layer(hdr);
     775        1485 :                 if (prev_layer>=0) {
     776         252 :                         if ((u8) prev_layer != val) {
     777             :                                 nb_frames=0;
     778             :                                 break;
     779             :                         }
     780             :                 }
     781        1485 :                 prev_layer = val;
     782             : 
     783        1485 :                 if (fsize + pos > size) {
     784          11 :                         nb_frames++;
     785          11 :                         break;
     786             :                 }
     787             : 
     788             :                 prev_pos = pos;
     789        1474 :                 nb_frames++;
     790        1474 :                 if (nb_frames>4) break;
     791             :                 if (size < fsize + pos) break;
     792        1413 :                 size -= fsize + pos;
     793        1413 :                 data += fsize + pos;
     794             :         }
     795             : 
     796        3074 :         if (nb_frames>=2) {
     797          63 :                 *score = (init_pos==0) ? GF_FPROBE_SUPPORTED : GF_FPROBE_MAYBE_SUPPORTED;
     798          63 :                 return "audio/mp3";
     799             :         }
     800        3011 :         if (nb_frames && has_id3) {
     801           0 :                 *score = GF_FPROBE_MAYBE_SUPPORTED;
     802           0 :                 return "audio/mp3";
     803             :         }
     804             :         return NULL;
     805             : }
     806             : 
     807             : static const GF_FilterCapability MP3DmxCaps[] =
     808             : {
     809             :         CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
     810             :         CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "mp3|mp2|mp1"),
     811             :         CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "audio/mp3|audio/x-mp3"),
     812             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
     813             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_MPEG_AUDIO),
     814             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_MPEG2_PART3),
     815             :         CAP_BOOL(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
     816             :         {0},
     817             :         CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
     818             :         CAP_BOOL(GF_CAPS_INPUT,GF_PROP_PID_UNFRAMED, GF_TRUE),
     819             :         CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_CODECID, GF_CODECID_MPEG_AUDIO),
     820             :         CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_CODECID, GF_CODECID_MPEG2_PART3),
     821             :         CAP_BOOL(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
     822             :         {0},
     823             :         //also declare generic file output for embedded files (cover art & co), but explicit to skip this cap in chain resolution
     824             :         CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
     825             :         CAP_UINT(GF_CAPS_OUTPUT | GF_CAPFLAG_LOADED_FILTER ,GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE)
     826             : };
     827             : 
     828             : 
     829             : 
     830             : #define OFFS(_n)        #_n, offsetof(GF_MP3DmxCtx, _n)
     831             : static const GF_FilterArgs MP3DmxArgs[] =
     832             : {
     833             :         { OFFS(index), "indexing window length", GF_PROP_DOUBLE, "1.0", NULL, 0},
     834             :         { OFFS(expart), "expose pictures as a dedicated video pid", GF_PROP_BOOL, "false", NULL, 0},
     835             :         {0}
     836             : };
     837             : 
     838             : 
     839             : GF_FilterRegister MP3DmxRegister = {
     840             :         .name = "rfmp3",
     841             :         GF_FS_SET_DESCRIPTION("MP3 reframer")
     842             :         GF_FS_SET_HELP("This filter parses MPEG-1/2 audio files/data and outputs corresponding audio PID and frames.")
     843             :         .private_size = sizeof(GF_MP3DmxCtx),
     844             :         .args = MP3DmxArgs,
     845             :         .finalize = mp3_dmx_finalize,
     846             :         SETCAPS(MP3DmxCaps),
     847             :         .configure_pid = mp3_dmx_configure_pid,
     848             :         .process = mp3_dmx_process,
     849             :         .probe_data = mp3_dmx_probe_data,
     850             :         .process_event = mp3_dmx_process_event
     851             : };
     852             : 
     853             : 
     854        2877 : const GF_FilterRegister *mp3_dmx_register(GF_FilterSession *session)
     855             : {
     856        2877 :         return &MP3DmxRegister;
     857             : }
     858             : 
     859             : #else
     860             : const GF_FilterRegister *mp3_dmx_register(GF_FilterSession *session)
     861             : {
     862             :         return NULL;
     863             : }
     864             : #endif // GPAC_DISABLE_AV_PARSERS
     865             : 

Generated by: LCOV version 1.13