LCOV - code coverage report
Current view: top level - filters - reframe_mhas.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 309 418 73.9 %
Date: 2021-04-29 23:48:07 Functions: 10 11 90.9 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2020-2021
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / MHAS 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             : 
      31             : static u32 USACSampleRates[] = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0, 0,
      32             :         57600, 51200, 40000, 38400, 34150, 28800, 25600, 20000, 19200, 17075, 14400, 12800, 9600};
      33             : 
      34             : static u32 nb_usac_sr = GF_ARRAY_LENGTH(USACSampleRates);
      35             : 
      36             : 
      37             : typedef struct
      38             : {
      39             :         u64 pos;
      40             :         Double duration;
      41             : } MHASIdx;
      42             : 
      43             : typedef struct
      44             : {
      45             :         //filter args
      46             :         Double index;
      47             :         Bool mpha;
      48             :         u32 pcksync;
      49             :         Bool nosync;
      50             : 
      51             :         //only one input pid declared
      52             :         GF_FilterPid *ipid;
      53             :         //only one output pid declared
      54             :         GF_FilterPid *opid;
      55             : 
      56             :         GF_BitStream *bs;
      57             :         u64 file_pos, cts, prev_cts;
      58             : 
      59             :         GF_Fraction64 duration;
      60             :         Double start_range;
      61             :         Bool in_seek;
      62             :         u32 timescale;
      63             :         Bool is_playing;
      64             :         Bool is_file;
      65             :         Bool initial_play_done, file_loaded;
      66             : 
      67             :         Bool initialized;
      68             : 
      69             :         u8 *mhas_buffer;
      70             :         u32 mhas_buffer_size, mhas_buffer_alloc, resume_from;
      71             :         u64 byte_offset;
      72             :         Bool buffer_too_small;
      73             : 
      74             :         GF_FilterPacket *src_pck;
      75             : 
      76             :         Bool recompute_cts;
      77             :         MHASIdx *indexes;
      78             :         u32 index_alloc_size, index_size;
      79             : 
      80             :         u32 sample_rate, frame_len, PL;
      81             :         s32 cicp_layout_idx, num_speakers;
      82             :         u32 nb_frames;
      83             : 
      84             :         u32 nb_unknown_pck;
      85             :         u32 bitrate;
      86             : } GF_MHASDmxCtx;
      87             : 
      88             : 
      89             : 
      90             : 
      91           5 : GF_Err mhas_dmx_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
      92             : {
      93             :         const GF_PropertyValue *p;
      94           5 :         GF_MHASDmxCtx *ctx = gf_filter_get_udta(filter);
      95             : 
      96           5 :         if (is_remove) {
      97           0 :                 ctx->ipid = NULL;
      98           0 :                 if (ctx->opid) {
      99           0 :                         gf_filter_pid_remove(ctx->opid);
     100           0 :                         ctx->opid = NULL;
     101             :                 }
     102             :                 return GF_OK;
     103             :         }
     104           5 :         if (! gf_filter_pid_check_caps(pid))
     105             :                 return GF_NOT_SUPPORTED;
     106             : 
     107           5 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_TIMESCALE);
     108           5 :         if (p) {
     109           0 :                 ctx->timescale = p->value.uint;
     110             :                 //if stream comes from TS or other muxed source unframed, force initial sync check
     111           0 :                 if (!ctx->ipid) {
     112           0 :                         p = gf_filter_pid_get_property(pid, GF_PROP_PID_UNFRAMED);
     113           0 :                         if (p && p->value.boolean)
     114           0 :                                 ctx->nosync = GF_TRUE;
     115             :                 }
     116             :         }
     117           5 :         ctx->ipid = pid;
     118           5 :         p = gf_filter_pid_get_property_str(pid, "nocts");
     119           5 :         if (p && p->value.boolean) ctx->recompute_cts = GF_TRUE;
     120           5 :         else ctx->recompute_cts = GF_FALSE;
     121             : 
     122           5 :         if (ctx->timescale && !ctx->opid) {
     123           0 :                 ctx->opid = gf_filter_pid_new(filter);
     124           0 :                 gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
     125           0 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_UNFRAMED, NULL);
     126             :         }
     127             :         return GF_OK;
     128             : }
     129             : 
     130        1358 : static void mhas_dmx_check_dur(GF_Filter *filter, GF_MHASDmxCtx *ctx)
     131             : {
     132             :         GF_Fraction64 duration;
     133             :         FILE *stream;
     134             :         GF_BitStream *bs;
     135             :         u32 frame_len, cur_dur;
     136             :         Bool mhas_sap;
     137             :         u64 mhas_last_cfg, rate;
     138             :         const GF_PropertyValue *p;
     139        1358 :         if (!ctx->opid || ctx->timescale || ctx->file_loaded) return;
     140             : 
     141           5 :         if (ctx->index<=0) {
     142           2 :                 ctx->file_loaded = GF_TRUE;
     143             :                 return;
     144             :         }
     145             : 
     146           3 :         p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILEPATH);
     147           3 :         if (!p || !p->value.string || !strncmp(p->value.string, "gmem://", 7)) {
     148           0 :                 ctx->is_file = GF_FALSE;
     149           0 :                 ctx->file_loaded = GF_TRUE;
     150             :                 return;
     151             :         }
     152           3 :         ctx->is_file = GF_TRUE;
     153             : 
     154           3 :         stream = gf_fopen(p->value.string, "rb");
     155           3 :         if (!stream) return;
     156             : 
     157           3 :         ctx->index_size = 0;
     158             : 
     159           3 :         bs = gf_bs_from_file(stream, GF_BITSTREAM_READ);
     160             :         duration.num = duration.den = 0;
     161             :         frame_len = cur_dur = 0;
     162             :         mhas_last_cfg = 0;
     163             : 
     164           3 :         while (gf_bs_available(bs)) {
     165           3 :                 u32 sync_code = gf_bs_peek_bits(bs, 24, 0);
     166           3 :                 if (sync_code == 0xC001A5) {
     167             :                         break;
     168             :                 }
     169           0 :                 gf_bs_skip_bytes(bs, 1);
     170             :         }
     171        8028 :         while (gf_bs_available(bs)) {
     172             :                 u64 mhas_pck_start, pay_start, parse_end, mhas_size;
     173             :                 u32 mhas_type;
     174             : 
     175        8025 :                 mhas_pck_start = gf_bs_get_position(bs);
     176        8025 :                 mhas_type = (u32) gf_mpegh_escaped_value(bs, 3, 8, 8);
     177        8025 :                 /*mhas_label = */gf_mpegh_escaped_value(bs, 2, 8, 32);
     178        8025 :                 mhas_size = gf_mpegh_escaped_value(bs, 11, 24, 24);
     179             : 
     180        8025 :                 pay_start = (u32) gf_bs_get_position(bs);
     181             : 
     182        8025 :                 if (!gf_bs_available(bs) ) break;
     183        8025 :                 if (mhas_size > gf_bs_available(bs)) break;
     184             : 
     185             :                 mhas_sap = 0;
     186             :                 //frame
     187        8025 :                 if (mhas_type==2) {
     188        3873 :                         mhas_sap = gf_bs_read_int(bs, 1);
     189        3873 :                         if (!mhas_last_cfg) mhas_sap = 0;
     190             :                 //config
     191        4152 :                 } else if (mhas_type==1) {
     192             :                         u32 sr = 0;
     193          93 :                         /*u32 pl = */gf_bs_read_u8(bs);
     194          93 :                         u32 idx = gf_bs_read_int(bs, 5);
     195          93 :                         if (idx==0x1f)
     196           0 :                                 duration.den = gf_bs_read_int(bs, 24);
     197          93 :                         else if (sr < nb_usac_sr) {
     198          93 :                                 duration.den = USACSampleRates[idx];
     199             :                         }
     200          93 :                         idx = gf_bs_read_int(bs, 3);
     201          93 :                         if ((idx==0) || (idx==2) ) frame_len = 768;
     202             :                         else frame_len = 1024;
     203             : 
     204             :                         mhas_last_cfg = mhas_pck_start;
     205             :                 }
     206             :                 //audio truncation
     207        4059 :                 else if (mhas_type==17) {
     208           0 :                         Bool isActive = gf_bs_read_int(bs, 1);
     209           0 :                         /*Bool ati_reserved = */gf_bs_read_int(bs, 1);
     210           0 :                         Bool trunc_from_begin = gf_bs_read_int(bs, 1);
     211           0 :                         u32 nb_trunc_samples = gf_bs_read_int(bs, 13);
     212           0 :                         if (isActive && !trunc_from_begin) {
     213           0 :                                 duration.num -= nb_trunc_samples;
     214             :                         }
     215             :                 }
     216        8025 :                 gf_bs_align(bs);
     217        8025 :                 parse_end = (u32) gf_bs_get_position(bs) - pay_start;
     218             :                 //remaining of packet payload
     219        8025 :                 gf_bs_skip_bytes(bs, mhas_size - parse_end);
     220             : 
     221             :                 //mhas_sap only set for frames
     222        8025 :                 if (mhas_sap && duration.den && (cur_dur >= ctx->index * duration.den) ) {
     223          45 :                         if (!ctx->index_alloc_size) ctx->index_alloc_size = 10;
     224          42 :                         else if (ctx->index_alloc_size == ctx->index_size) ctx->index_alloc_size *= 2;
     225          45 :                         ctx->indexes = gf_realloc(ctx->indexes, sizeof(MHASIdx)*ctx->index_alloc_size);
     226          45 :                         ctx->indexes[ctx->index_size].pos = mhas_last_cfg;
     227          45 :                         ctx->indexes[ctx->index_size].duration = ((Double) duration.num) / duration.den;
     228          45 :                         ctx->index_size ++;
     229             :                         cur_dur = 0;
     230             :                 }
     231        8025 :                 if (mhas_type==2) {
     232        3873 :                         duration.num += frame_len;
     233        3873 :                         cur_dur += frame_len;
     234             :                         mhas_last_cfg = 0;
     235             :                 }
     236             :         }
     237             : 
     238           3 :         rate = gf_bs_get_position(bs);
     239           3 :         gf_bs_del(bs);
     240           3 :         gf_fclose(stream);
     241             : 
     242           3 :         if (!ctx->duration.num || (ctx->duration.num  * duration.den != duration.num * ctx->duration.den)) {
     243           3 :                 ctx->duration = duration;
     244           3 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, & PROP_FRAC64(ctx->duration));
     245             : 
     246           3 :                 if (duration.num && !gf_sys_is_test_mode() ) {
     247           0 :                         rate *= 8 * ctx->duration.den;
     248           0 :                         rate /= ctx->duration.num;
     249           0 :                         ctx->bitrate = (u32) rate;
     250             :                 }
     251             :         }
     252             : 
     253           3 :         p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILE_CACHED);
     254           3 :         if (p && p->value.boolean) ctx->file_loaded = GF_TRUE;
     255           3 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CAN_DATAREF, & PROP_BOOL(GF_TRUE ) );
     256             : }
     257             : 
     258         142 : static void mhas_dmx_check_pid(GF_Filter *filter, GF_MHASDmxCtx *ctx, u32 PL, u32 sample_rate, u32 frame_len, s32 CICPspeakerLayoutIdx, s32 numSpeakers, u8 *dsi, u32 dsi_size)
     259             : {
     260             :         u32 nb_channels;
     261             :         u64 chan_layout;
     262         142 :         if (!ctx->opid) {
     263           5 :                 ctx->opid = gf_filter_pid_new(filter);
     264           5 :                 mhas_dmx_check_dur(filter, ctx);
     265             :         } else {
     266         137 :                 if ((ctx->frame_len == frame_len)
     267         137 :                         && (ctx->PL == PL)
     268         137 :                         && (ctx->sample_rate == sample_rate)
     269         137 :                         && (ctx->cicp_layout_idx == CICPspeakerLayoutIdx)
     270         137 :                         && (ctx->num_speakers == numSpeakers)
     271             :                 ) {
     272             :                         return;
     273             :                 }
     274             :         }
     275           5 :         ctx->frame_len = frame_len;
     276           5 :         ctx->PL = PL;
     277           5 :         ctx->sample_rate = sample_rate;
     278           5 :         ctx->cicp_layout_idx = CICPspeakerLayoutIdx;
     279           5 :         ctx->num_speakers = numSpeakers;
     280             : 
     281             :         chan_layout = 0;
     282             :         nb_channels = 0;
     283           5 :         if (CICPspeakerLayoutIdx>=0) {
     284           5 :                 chan_layout = gf_audio_fmt_get_layout_from_cicp(CICPspeakerLayoutIdx);
     285           5 :                 nb_channels = gf_audio_fmt_get_num_channels_from_layout(chan_layout);
     286           0 :         } else if (numSpeakers>=0) {
     287           0 :                 nb_channels = numSpeakers;
     288             :         }
     289             : 
     290             :         //copy properties at init or reconfig
     291           5 :         gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
     292           5 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, & PROP_UINT( GF_STREAM_AUDIO));
     293           5 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_UNFRAMED, NULL );
     294           5 :         if (ctx->is_file && ctx->index) {
     295           3 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PLAYBACK_MODE, & PROP_UINT(GF_PLAYBACK_MODE_FASTFORWARD) );
     296             :         }
     297           5 :         if (ctx->duration.num)
     298           3 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, & PROP_FRAC64(ctx->duration));
     299             : 
     300           5 :         if (!ctx->timescale) gf_filter_pid_set_name(ctx->opid, "audio");
     301             : 
     302           5 :         if (ctx->mpha) {
     303           2 :                 u8 *data = gf_malloc(sizeof(u8) * (dsi_size+5) );
     304           2 :                 if (!data) return;
     305           2 :                 data[0] = 1;
     306           2 :                 data[1] = PL;
     307           2 :                 data[2] = CICPspeakerLayoutIdx;
     308           2 :                 data[3] = dsi_size>>8;
     309           2 :                 data[4] = dsi_size&0xFF;
     310           2 :                 memcpy(data+5, dsi, dsi_size);
     311           2 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, & PROP_UINT( GF_CODECID_MPHA ) );
     312           2 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DECODER_CONFIG, & PROP_DATA_NO_COPY( data, (dsi_size+5) ) );
     313             :         } else {
     314           3 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, & PROP_UINT( GF_CODECID_MHAS ) );
     315             :         }
     316             : 
     317           5 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_TIMESCALE, & PROP_UINT(ctx->timescale ? ctx->timescale : ctx->sample_rate));
     318           5 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLE_RATE, & PROP_UINT(ctx->sample_rate));
     319           5 :         if (chan_layout)
     320           5 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CHANNEL_LAYOUT, & PROP_LONGUINT(chan_layout) );
     321           5 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_NUM_CHANNELS, & PROP_UINT(nb_channels) );
     322           5 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLES_PER_FRAME, & PROP_UINT(ctx->frame_len) );
     323             : 
     324           5 :         if (ctx->bitrate) {
     325           0 :                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_BITRATE, & PROP_UINT(ctx->bitrate));
     326             :         }
     327             : }
     328             : 
     329        5290 : static Bool mhas_dmx_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
     330             : {
     331             :         u32 i;
     332             :         GF_FilterEvent fevt;
     333        5290 :         GF_MHASDmxCtx *ctx = gf_filter_get_udta(filter);
     334             : 
     335        5290 :         if (evt->base.on_pid != ctx->opid) return GF_TRUE;
     336             : 
     337           6 :         switch (evt->base.type) {
     338           5 :         case GF_FEVT_PLAY:
     339           5 :                 if (!ctx->is_playing) {
     340           5 :                         ctx->is_playing = GF_TRUE;
     341             :                 }
     342           5 :                 if (! ctx->is_file) {
     343           2 :                         if (evt->play.start_range || ctx->initial_play_done) {
     344           0 :                                 ctx->mhas_buffer_size = 0;
     345           0 :                                 ctx->resume_from = 0;
     346             :                         }
     347           2 :                         ctx->initial_play_done = GF_TRUE;
     348           2 :                         return GF_FALSE;
     349             :                 }
     350           3 :                 mhas_dmx_check_dur(filter, ctx);
     351             : 
     352           3 :                 ctx->start_range = evt->play.start_range;
     353           3 :                 ctx->in_seek = GF_TRUE;
     354           3 :                 ctx->file_pos = 0;
     355           3 :                 if (ctx->start_range) {
     356           0 :                         for (i=1; i<ctx->index_size; i++) {
     357           0 :                                 if (ctx->indexes[i].duration>ctx->start_range) {
     358           0 :                                         ctx->cts = (u64) (ctx->indexes[i-1].duration * ctx->sample_rate);
     359           0 :                                         ctx->file_pos = ctx->indexes[i-1].pos;
     360           0 :                                         break;
     361             :                                 }
     362             :                         }
     363             :                 }
     364           3 :                 if (!ctx->initial_play_done) {
     365           3 :                         ctx->initial_play_done = GF_TRUE;
     366             :                         //seek will not change the current source state, don't send a seek
     367           3 :                         if (!ctx->file_pos)
     368             :                                 return GF_TRUE;
     369             :                 }
     370           0 :                 ctx->mhas_buffer_size = 0;
     371           0 :                 ctx->resume_from = 0;
     372             :                 //post a seek
     373           0 :                 GF_FEVT_INIT(fevt, GF_FEVT_SOURCE_SEEK, ctx->ipid);
     374           0 :                 fevt.seek.start_offset = ctx->file_pos;
     375           0 :                 gf_filter_pid_send_event(ctx->ipid, &fevt);
     376             : 
     377             :                 //cancel event
     378           0 :                 return GF_TRUE;
     379             : 
     380           1 :         case GF_FEVT_STOP:
     381           1 :                 ctx->is_playing = GF_FALSE;
     382           1 :                 if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
     383           1 :                 ctx->src_pck = NULL;
     384             :                 //don't cancel event
     385           1 :                 return GF_FALSE;
     386             : 
     387             :         case GF_FEVT_SET_SPEED:
     388             :                 //cancel event
     389             :                 return GF_TRUE;
     390             :         default:
     391             :                 break;
     392             :         }
     393             :         //by default don't cancel event - to rework once we have downloading in place
     394           0 :         return GF_FALSE;
     395             : }
     396             : 
     397             : static GFINLINE void mhas_dmx_update_cts(GF_MHASDmxCtx *ctx)
     398             : {
     399        5190 :         if (ctx->timescale) {
     400           0 :                 u64 inc = ctx->frame_len;
     401           0 :                 inc *= ctx->timescale;
     402           0 :                 inc /= ctx->sample_rate;
     403           0 :                 ctx->cts += inc;
     404             :         } else {
     405        5190 :                 ctx->cts += ctx->frame_len;
     406             :         }
     407             : }
     408             : 
     409             : #ifndef GPAC_DISABLE_LOG
     410           0 : static const char *mhas_pck_name(u32 pck_type)
     411             : {
     412           0 :         switch (pck_type) {
     413             :         case 0: return "FILL_DATA";
     414           0 :         case 1: return "MPEGH3DACFG";
     415           0 :         case 2: return "MPEGH3DAFRAME";
     416           0 :         case 3: return "AUDIOSCENEINFO";
     417           0 :         case 6: return "SYNC";
     418           0 :         case 7: return "SYNCGAP";
     419           0 :         case 8: return "MARKER";
     420           0 :         case 9: return "CRC16";
     421           0 :         case 10: return "CRC32";
     422           0 :         case 11: return "DESCRIPTOR";
     423           0 :         case 12: return "USERINTERACTION";
     424           0 :         case 13: return "LOUDNESS_DRC";
     425           0 :         case 14: return "BUFFERINFO";
     426           0 :         case 15: return "GLOBAL_CRC16";
     427           0 :         case 16: return "GLOBAL_CRC32";
     428           0 :         case 17: return "AUDIOTRUNCATION";
     429           0 :         case 18: return "GENDATA";
     430           0 :         case 4:
     431             :         case 5:
     432             :         default:
     433           0 :                 return "ISOReserved";
     434             :         }
     435             :         return "error";
     436             : }
     437             : #endif
     438             : 
     439        5387 : GF_Err mhas_dmx_process(GF_Filter *filter)
     440             : {
     441        5387 :         GF_MHASDmxCtx *ctx = gf_filter_get_udta(filter);
     442             :         GF_FilterPacket *in_pck;
     443             :         u8 *output;
     444             :         u8 *start;
     445             :         Bool final_flush=GF_FALSE;
     446             :         u32 pck_size, remain, prev_pck_size;
     447             :         u64 cts = GF_FILTER_NO_TS;
     448             :         u32 au_start = 0;
     449             :         u32 consumed = 0;
     450             :         u32 nb_trunc_samples = 0;
     451             :         Bool trunc_from_begin = 0;
     452             :         Bool has_cfg = 0;
     453             : 
     454             :         //always reparse duration
     455        5387 :         if (!ctx->duration.num)
     456        1350 :                 mhas_dmx_check_dur(filter, ctx);
     457             : 
     458        5387 :         if (ctx->opid && !ctx->is_playing)
     459             :                 return GF_OK;
     460             : 
     461        5367 :         in_pck = gf_filter_pid_get_packet(ctx->ipid);
     462        5367 :         if (!in_pck) {
     463          84 :                 if (gf_filter_pid_is_eos(ctx->ipid)) {
     464          84 :                         if (!ctx->mhas_buffer_size) {
     465           4 :                                 if (ctx->opid)
     466           4 :                                         gf_filter_pid_set_eos(ctx->opid);
     467           4 :                                 if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
     468           4 :                                 ctx->src_pck = NULL;
     469           4 :                                 return GF_EOS;
     470             :                         }
     471             :                         final_flush = GF_TRUE;
     472           0 :                 } else if (!ctx->resume_from) {
     473             :                         return GF_OK;
     474             :                 }
     475             :         }
     476             : 
     477        5363 :         prev_pck_size = ctx->mhas_buffer_size;
     478        5363 :         if (ctx->resume_from)
     479             :                 in_pck = NULL;
     480             : 
     481         197 :         if (in_pck) {
     482         197 :                 u8 *data = (u8 *) gf_filter_pck_get_data(in_pck, &pck_size);
     483             : 
     484         197 :                 if (ctx->byte_offset != GF_FILTER_NO_BO) {
     485         197 :                         u64 byte_offset = gf_filter_pck_get_byte_offset(in_pck);
     486         197 :                         if (!ctx->mhas_buffer_size) {
     487           5 :                                 ctx->byte_offset = byte_offset;
     488         192 :                         } else if (ctx->byte_offset + ctx->mhas_buffer_size != byte_offset) {
     489           0 :                                 ctx->byte_offset = GF_FILTER_NO_BO;
     490           0 :                                 if ((byte_offset != GF_FILTER_NO_BO) && (byte_offset>ctx->mhas_buffer_size) ) {
     491           0 :                                         ctx->byte_offset = byte_offset - ctx->mhas_buffer_size;
     492             :                                 }
     493             :                         }
     494             :                 }
     495             : 
     496         197 :                 if (ctx->mhas_buffer_size + pck_size > ctx->mhas_buffer_alloc) {
     497          21 :                         ctx->mhas_buffer_alloc = ctx->mhas_buffer_size + pck_size;
     498          21 :                         ctx->mhas_buffer = gf_realloc(ctx->mhas_buffer, ctx->mhas_buffer_alloc);
     499             :                 }
     500         197 :                 memcpy(ctx->mhas_buffer + ctx->mhas_buffer_size, data, pck_size);
     501         197 :                 ctx->mhas_buffer_size += pck_size;
     502             :         }
     503             : 
     504             :         //input pid sets some timescale - we flushed pending data , update cts
     505        5363 :         if (ctx->timescale && in_pck) {
     506           0 :                 cts = gf_filter_pck_get_cts(in_pck);
     507             :         }
     508             : 
     509           0 :         if (cts == GF_FILTER_NO_TS) {
     510             :                 //avoids updating cts
     511             :                 prev_pck_size = 0;
     512             :         }
     513             : 
     514        5363 :         remain = ctx->mhas_buffer_size;
     515        5363 :         start = ctx->mhas_buffer;
     516             : 
     517        5363 :         if (ctx->resume_from) {
     518        5166 :                 start += ctx->resume_from - 1;
     519        5166 :                 remain -= ctx->resume_from - 1;
     520        5166 :                 ctx->resume_from = 0;
     521             :         }
     522             : 
     523        5363 :         while (ctx->nosync && (remain>3)) {
     524             :                 //wait till we have a frame header
     525           5 :                 u8 *hdr_start = memchr(start, 0xC0, remain);
     526           5 :                 if (!hdr_start) {
     527             :                         remain=0;
     528             :                         break;
     529             :                 }
     530           5 :                 if ((hdr_start[1]==0x01) && (hdr_start[2]==0xA5)) {
     531           5 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[MHASDmx] Sync found !\n"));
     532           5 :                         ctx->nosync = GF_FALSE;
     533           5 :                         break;
     534             :                 }
     535           0 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[MHASDmx] not sync, skipping byte\n"));
     536           0 :                 start++;
     537           0 :                 remain--;
     538             :         }
     539        5363 :         if (ctx->nosync)
     540             :                 goto skip;
     541             : 
     542        5363 :         gf_bs_reassign_buffer(ctx->bs, start, remain);
     543        5363 :         ctx->buffer_too_small = GF_FALSE;
     544             : 
     545             :         //MHAS packet
     546       16543 :         while (remain > consumed) {
     547             :                 u32 pay_start, parse_end, mhas_size, mhas_label;
     548             :                 Bool mhas_sap = 0;
     549             :                 u32 mhas_type;
     550       11180 :                 if (!ctx->is_playing && ctx->opid) {
     551           5 :                         ctx->resume_from = 1;
     552             :                         consumed = 0;
     553             :                         break;
     554             :                 }
     555             : 
     556       11175 :                 mhas_type = (u32) gf_mpegh_escaped_value(ctx->bs, 3, 8, 8);
     557       11175 :                 mhas_label = (u32) gf_mpegh_escaped_value(ctx->bs, 2, 8, 32);
     558       11175 :                 mhas_size = (u32) gf_mpegh_escaped_value(ctx->bs, 11, 24, 24);
     559             : 
     560       11175 :                 if (ctx->buffer_too_small)
     561             :                         break;
     562             : 
     563             : 
     564       11167 :                 if (mhas_type>18) {
     565           0 :                         ctx->nb_unknown_pck++;
     566           0 :                         if (ctx->nb_unknown_pck > ctx->pcksync) {
     567           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[MHASDmx] %d packets of unknwon type, considering sync was lost\n"));
     568             :                                 consumed = 0;
     569           0 :                                 ctx->nosync = GF_TRUE;
     570           0 :                                 ctx->nb_unknown_pck = 0;
     571             :                                 break;
     572             :                         }
     573       11167 :                 } else if (!mhas_size) {
     574           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[MHASDmx] MHAS packet with 0 payload size, considering sync was lost\n"));
     575             :                         consumed = 0;
     576           0 :                         ctx->nosync = GF_TRUE;
     577           0 :                         ctx->nb_unknown_pck = 0;
     578             :                         break;
     579             :                 }
     580             : 
     581       11167 :                 pay_start = (u32) gf_bs_get_position(ctx->bs);
     582             : 
     583       11167 :                 if (ctx->buffer_too_small) break;
     584       11167 :                 if (mhas_size > gf_bs_available(ctx->bs)) {
     585             :                         //incomplete frame, keep in buffer
     586         185 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[MHASDmx] incomplete packet type %d %s label "LLU" size "LLU" - keeping in buffer\n", mhas_type, mhas_pck_name(mhas_type), mhas_label, mhas_size));
     587             :                         break;
     588             :                 }
     589             :                 //frame
     590       10982 :                 if (mhas_type==2) {
     591        5190 :                         mhas_sap = gf_bs_peek_bits(ctx->bs, 1, 0);
     592        5190 :                         ctx->nb_unknown_pck = 0;
     593             :                 }
     594             :                 //config
     595        5792 :                 else if (mhas_type==1) {
     596             :                         s32 CICPspeakerLayoutIdx = -1;
     597             :                         s32 numSpeakers = -1;
     598             :                         u32 sr = 0;
     599             :                         u32 frame_len;
     600         142 :                         u32 pl = gf_bs_read_u8(ctx->bs);
     601         142 :                         u32 idx = gf_bs_read_int(ctx->bs, 5);
     602         142 :                         if (idx==0x1f)
     603           0 :                                 sr = gf_bs_read_int(ctx->bs, 24);
     604         142 :                         else if (sr < nb_usac_sr) {
     605         142 :                                 sr = USACSampleRates[idx];
     606             :                         }
     607         142 :                         ctx->nb_unknown_pck = 0;
     608         142 :                         idx = gf_bs_read_int(ctx->bs, 3);
     609         142 :                         if ((idx==0) || (idx==2) ) frame_len = 768;
     610             :                         else frame_len = 1024;
     611         142 :                         gf_bs_read_int(ctx->bs, 1);
     612         142 :                         gf_bs_read_int(ctx->bs, 1);
     613             : 
     614             :                         //speaker config
     615         142 :                         u32 speakerLayoutType = gf_bs_read_int(ctx->bs, 2);
     616         142 :                         if (speakerLayoutType == 0) {
     617         142 :                                 CICPspeakerLayoutIdx = gf_bs_read_int(ctx->bs, 6);
     618             :                         } else {
     619           0 :                                 numSpeakers = (s32) gf_mpegh_escaped_value(ctx->bs, 5, 8, 16) + 1;
     620             :                                 //TODO ...
     621             :                         }
     622             : 
     623         142 :                         mhas_dmx_check_pid(filter, ctx, pl, sr, frame_len, CICPspeakerLayoutIdx, numSpeakers, start + pay_start, (u32) mhas_size);
     624             : 
     625             :                         has_cfg = GF_TRUE;
     626             :                 }
     627             :                 //audio truncation
     628        5650 :                 else if (mhas_type==17) {
     629           0 :                         Bool isActive = gf_bs_read_int(ctx->bs, 1);
     630           0 :                         /*Bool ati_reserved = */gf_bs_read_int(ctx->bs, 1);
     631           0 :                         trunc_from_begin = gf_bs_read_int(ctx->bs, 1);
     632           0 :                         nb_trunc_samples = gf_bs_read_int(ctx->bs, 13);
     633           0 :                         if (!isActive) {
     634             :                                 nb_trunc_samples = 0;
     635             :                         }
     636             :                 }
     637             :                 //sync, syncgap
     638        5650 :                 else if ((mhas_type==6) || (mhas_type==7)) {
     639        5376 :                         ctx->nb_unknown_pck = 0;
     640             :                 }
     641             : #if 0
     642             :                 //MARKER
     643             :                 else if (mhas_type==8) {
     644             :                         u8 marker_type = gf_bs_read_u8(ctx->bs);
     645             :                         //config reload force
     646             :                         if (marker_type==0x01) {}
     647             :                         //SAP
     648             :                         else if (marker_type==0x02) {
     649             :                                 has_marker = GF_TRUE;
     650             :                         }
     651             :                 }
     652             : #endif
     653             : 
     654       10982 :                 gf_bs_align(ctx->bs);
     655       10982 :                 parse_end = (u32) gf_bs_get_position(ctx->bs) - pay_start;
     656             :                 //remaining of packet payload
     657       10982 :                 gf_bs_skip_bytes(ctx->bs, mhas_size - parse_end);
     658             : 
     659       10982 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[MHASDmx] MHAS Packet type %d %s label "LLU" size "LLU"\n", mhas_type, mhas_pck_name(mhas_type), mhas_label, mhas_size));
     660             : 
     661       10982 :                 if (ctx->timescale && !prev_pck_size && (cts != GF_FILTER_NO_TS) ) {
     662           0 :                         ctx->cts = cts;
     663             :                         cts = GF_FILTER_NO_TS;
     664             :                 }
     665             : 
     666             :                 //frame
     667       10982 :                 if ((mhas_type==2) && ctx->opid) {
     668             :                         GF_FilterPacket *dst;
     669        5190 :                         u64 pck_dur = ctx->frame_len;
     670             : 
     671             : 
     672             :                         u32 au_size;
     673        5190 :                         if (ctx->mpha) {
     674             :                                 au_start = pay_start;
     675             :                                 au_size = mhas_size;
     676             :                         } else {
     677        2608 :                                 au_size = (u32) gf_bs_get_position(ctx->bs) - au_start;
     678             :                         }
     679             : 
     680        5190 :                         if (nb_trunc_samples) {
     681           0 :                                 if (trunc_from_begin) {
     682           0 :                                         if (!ctx->nb_frames) {
     683           0 :                                                 s64 offset = trunc_from_begin;
     684           0 :                                                 if (ctx->timescale) {
     685           0 :                                                         offset *= ctx->timescale;
     686           0 :                                                         offset /= ctx->sample_rate;
     687             :                                                 }
     688           0 :                                                 gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DELAY , &PROP_LONGSINT( -offset));
     689             :                                         }
     690             :                                 } else {
     691           0 :                                         pck_dur -= nb_trunc_samples;
     692             :                                 }
     693             :                                 nb_trunc_samples = 0;
     694             :                         }
     695             : 
     696        5190 :                         if (ctx->timescale) {
     697           0 :                                 pck_dur *= ctx->timescale;
     698           0 :                                 pck_dur /= ctx->sample_rate;
     699             :                         }
     700             : 
     701        5190 :                         dst = gf_filter_pck_new_alloc(ctx->opid, au_size, &output);
     702        5190 :                         if (!dst) break;
     703        5190 :                         if (ctx->src_pck) gf_filter_pck_merge_properties(ctx->src_pck, dst);
     704             : 
     705        5190 :                         memcpy(output, start + au_start, au_size);
     706        5190 :                         if (!has_cfg)
     707             :                                 mhas_sap = 0;
     708             : 
     709         125 :                         if (mhas_sap) {
     710         125 :                                 gf_filter_pck_set_sap(dst, GF_FILTER_SAP_1);
     711             :                         }
     712        5190 :                         gf_filter_pck_set_dts(dst, ctx->cts);
     713        5190 :                         gf_filter_pck_set_cts(dst, ctx->cts);
     714        5190 :                         gf_filter_pck_set_duration(dst, (u32) pck_dur);
     715        5190 :                         if (ctx->byte_offset != GF_FILTER_NO_BO) {
     716        5190 :                                 u64 offset = (u64) (start - ctx->mhas_buffer);
     717        5190 :                                 offset += ctx->byte_offset + au_start;
     718        5190 :                                 gf_filter_pck_set_byte_offset(dst, offset);
     719             :                         }
     720        5190 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[MHASDmx] Send AU CTS "LLU" size %d dur %d sap %d\n", ctx->cts, au_size, (u32) pck_dur, mhas_sap));
     721        5190 :                         gf_filter_pck_send(dst);
     722             : 
     723        5190 :                         au_start += au_size;
     724             :                         consumed = au_start;
     725        5190 :                         ctx->nb_frames ++;
     726             : 
     727             :                         mhas_dmx_update_cts(ctx);
     728             :                         has_cfg = 0;
     729             : 
     730        5190 :                         if (prev_pck_size) {
     731           0 :                                 u64 next_pos = (u64) (start + au_start - ctx->mhas_buffer);
     732             :                                 //next will be in new packet
     733           0 :                                 if (prev_pck_size <= next_pos) {
     734             :                                         prev_pck_size = 0;
     735           0 :                                         if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
     736           0 :                                         ctx->src_pck = in_pck;
     737           0 :                                         if (in_pck)
     738           0 :                                                 gf_filter_pck_ref_props(&ctx->src_pck);
     739             : 
     740           0 :                                         if (ctx->timescale && (cts != GF_FILTER_NO_TS) ) {
     741           0 :                                                 ctx->cts = cts;
     742             :                                                 cts = GF_FILTER_NO_TS;
     743             :                                         }
     744             :                                 }
     745             :                         }
     746        5190 :                         if (remain==consumed)
     747             :                                 break;
     748             : 
     749        5186 :                         if (gf_filter_pid_would_block(ctx->opid)) {
     750        5161 :                                 ctx->resume_from = 1;
     751             :                                 final_flush = GF_FALSE;
     752        5161 :                                 break;
     753             :                         }
     754             :                 }
     755             :         }
     756        5358 :         if (consumed) {
     757             :                 assert(remain>=consumed);
     758        5166 :                 remain -= consumed;
     759        5166 :                 start += consumed;
     760             :         }
     761             : 
     762        5555 : skip:
     763             : 
     764        5363 :         if (remain < ctx->mhas_buffer_size) {
     765        5166 :                 memmove(ctx->mhas_buffer, start, remain);
     766             :                 //update byte offset
     767        5166 :                 if (ctx->byte_offset != GF_FILTER_NO_BO)
     768        5166 :                         ctx->byte_offset += ctx->mhas_buffer_size - remain;
     769             :         }
     770        5363 :         ctx->mhas_buffer_size = remain;
     771        5363 :         if (final_flush)
     772           4 :                 ctx->mhas_buffer_size = 0;
     773             : 
     774        5363 :         if (!ctx->mhas_buffer_size) {
     775           4 :                 if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
     776           4 :                 ctx->src_pck = NULL;
     777             :         }
     778             : 
     779        5363 :         if (in_pck)
     780         197 :                 gf_filter_pid_drop_packet(ctx->ipid);
     781             : 
     782             :         return GF_OK;
     783             : }
     784             : 
     785           8 : static void mhas_buffer_too_small(void *udta)
     786             : {
     787             :         GF_MHASDmxCtx *ctx = (GF_MHASDmxCtx *) udta;
     788           8 :         ctx->buffer_too_small = GF_TRUE;
     789           8 : }
     790             : 
     791           5 : static GF_Err mhas_dmx_initialize(GF_Filter *filter)
     792             : {
     793           5 :         GF_MHASDmxCtx *ctx = gf_filter_get_udta(filter);
     794           5 :         ctx->bs = gf_bs_new((u8 *)ctx, 1, GF_BITSTREAM_READ);
     795           5 :         gf_bs_set_eos_callback(ctx->bs, mhas_buffer_too_small, ctx);
     796           5 :         return GF_OK;
     797             : }
     798           5 : static void mhas_dmx_finalize(GF_Filter *filter)
     799             : {
     800           5 :         GF_MHASDmxCtx *ctx = gf_filter_get_udta(filter);
     801           5 :         if (ctx->bs) gf_bs_del(ctx->bs);
     802           5 :         if (ctx->indexes) gf_free(ctx->indexes);
     803           5 :         if (ctx->mhas_buffer) gf_free(ctx->mhas_buffer);
     804           5 :         if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
     805           5 : }
     806             : 
     807             : 
     808        3065 : static const char *mhas_dmx_probe_data(const u8 *data, u32 size, GF_FilterProbeScore *score)
     809             : {
     810             :         s32 sync_pos = -1;
     811             :         GF_BitStream *bs;
     812             :         u32 nb_mhas_cfg = 0;
     813             :         u32 nb_mhas_frames = 0;
     814             :         u32 nb_mhas_unknown = 0;
     815             :         const u8 *ptr = data;
     816       42866 :         while (ptr) {
     817       39801 :                 u32 pos = (u32) (ptr - data);
     818       39801 :                 const u8 *sync_start = memchr(ptr, 0xC0, size - pos);
     819       39801 :                 if (!sync_start) return NULL;
     820       36741 :                 if ((sync_start[1]== 0x01) && (sync_start[2]==0xA5)) {
     821           5 :                         sync_pos = pos;
     822           5 :                         break;
     823             :                 }
     824       36736 :                 ptr = sync_start+1;
     825             :         }
     826           5 :         if (sync_pos<0) return NULL;
     827           5 :         bs = gf_bs_new(data, size, GF_BITSTREAM_READ);
     828           5 :         gf_bs_skip_bytes(bs, sync_pos);
     829             : 
     830         290 :         while (gf_bs_available(bs)) {
     831         285 :                 u32 type = (u32) gf_mpegh_escaped_value(bs, 3, 8, 8);
     832         285 :                 /*u64 label = */gf_mpegh_escaped_value(bs, 2, 8, 32);
     833         285 :                 u64 mh_size = gf_mpegh_escaped_value(bs, 11, 24, 24);
     834         285 :                 if (mh_size > gf_bs_available(bs))
     835             :                         break;
     836             :                 //MHAS config
     837         280 :                 if (type==1) nb_mhas_cfg++;
     838         275 :                 else if (type==2) nb_mhas_frames++;
     839         145 :                 else if (type>18) nb_mhas_unknown++;
     840         280 :                 gf_bs_skip_bytes(bs, mh_size);
     841             :         }
     842           5 :         gf_bs_del(bs);
     843           5 :         if (!nb_mhas_unknown && nb_mhas_cfg && nb_mhas_frames) {
     844           5 :                 *score = GF_FPROBE_SUPPORTED;
     845           5 :                 return "audio/mpegh";
     846             :         }
     847             :         return NULL;
     848             : }
     849             : 
     850             : static const GF_FilterCapability MHASDmxCaps[] =
     851             : {
     852             :         CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
     853             :         CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "mhas"),
     854             :         CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "audio/mpegh"),
     855             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
     856             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_MHAS),
     857             :         CAP_BOOL(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
     858             :         {0},
     859             :         CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
     860             :         CAP_BOOL(GF_CAPS_INPUT,GF_PROP_PID_UNFRAMED, GF_TRUE),
     861             :         CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_CODECID, GF_CODECID_MHAS),
     862             :         CAP_BOOL(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
     863             : };
     864             : 
     865             : 
     866             : 
     867             : #define OFFS(_n)        #_n, offsetof(GF_MHASDmxCtx, _n)
     868             : static const GF_FilterArgs MHASDmxArgs[] =
     869             : {
     870             :         { OFFS(index), "indexing window length", GF_PROP_DOUBLE, "1.0", NULL, 0},
     871             :         { OFFS(mpha), "demux MHAS and only forward audio frames", GF_PROP_BOOL, "false", NULL, 0},
     872             :         { OFFS(pcksync), "number of unknwon packets to tolerate before considering sync is lost", GF_PROP_UINT, "4", NULL, 0},
     873             :         { OFFS(nosync), "initial sync state - see filter help", GF_PROP_BOOL, "true", NULL, 0},
     874             : 
     875             :         {0}
     876             : };
     877             : 
     878             : 
     879             : GF_FilterRegister MHASDmxRegister = {
     880             :         .name = "rfmhas",
     881             :         GF_FS_SET_DESCRIPTION("MPEH-H Audio Stream reframer")
     882             :         GF_FS_SET_HELP("This filter parses MHAS files/data and outputs corresponding audio PID and frames.\n"
     883             :                 "By default, the filter expects a MHAS stream with SYNC packets set, otherwise tune-in will fail. Using [-nosync]()=false can help parsing bitstreams with no SYNC packets.\n"
     884             :                 "The default behavior is to dispatch a framed MHAS bitstream. To demultiplex into a raw MPEG-H Audio, use [-mpha]().\n"
     885             :                 )
     886             :         .private_size = sizeof(GF_MHASDmxCtx),
     887             :         .args = MHASDmxArgs,
     888             :         .finalize = mhas_dmx_finalize,
     889             :         .initialize = mhas_dmx_initialize,
     890             :         SETCAPS(MHASDmxCaps),
     891             :         .configure_pid = mhas_dmx_configure_pid,
     892             :         .process = mhas_dmx_process,
     893             :         .probe_data = mhas_dmx_probe_data,
     894             :         .process_event = mhas_dmx_process_event
     895             : };
     896             : 
     897             : 
     898        2877 : const GF_FilterRegister *mhas_dmx_register(GF_FilterSession *session)
     899             : {
     900        2877 :         return &MHASDmxRegister;
     901             : }

Generated by: LCOV version 1.13