LCOV - code coverage report
Current view: top level - compositor - audio_input.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 116 157 73.9 %
Date: 2021-04-29 23:48:07 Functions: 14 14 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-2018
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / Scene Compositor sub-project
       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/internal/compositor_dev.h>
      27             : 
      28             : #define ENABLE_EARLY_FRAME_DETECTION
      29             : 
      30             : /*diff time in ms to consider an audio frame too late and drop it - we should try to dynamically figure this out
      31             : since the drift may be high on TS for example, where PTS-PCR>500ms is quite common*/
      32             : #define MAX_RESYNC_TIME         1000
      33             : //if drift between audio object time and clock varies more is than this value (in ms) between two drift computation, clock is adjusted. We don't adjust for lower values otherwise we would
      34             : //introduce oscillations in the clock and non-smooth playback
      35             : #define MIN_DRIFT_ADJUST        75
      36             : 
      37             : 
      38       39475 : static u8 *gf_audio_input_fetch_frame(void *callback, u32 *size, u32 *planar_size, u32 audio_delay_ms)
      39             : {
      40             :         char *frame;
      41             :         u32 obj_time, ts;
      42             :         s32 drift;
      43             :         Fixed speed;
      44             :         Bool done;
      45             :         GF_AudioInput *ai = (GF_AudioInput *) callback;
      46             :         /*even if the stream is signaled as finished we must check it, because it may have been restarted by a mediaControl*/
      47       39475 :         if (!ai->stream) return NULL;
      48             : 
      49       39475 :         done = ai->stream_finished;
      50       39475 :         ai->input_ifce.is_buffering = GF_FALSE;
      51             : 
      52       39475 :         frame = gf_mo_fetch_data(ai->stream, ai->compositor->audio_renderer->non_rt_output ? GF_MO_FETCH_PAUSED : GF_MO_FETCH, 0, &ai->stream_finished, &ts, size, NULL, NULL, NULL, planar_size);
      53             :         /*invalidate scene on end of stream to refresh audio graph*/
      54       39475 :         if (done != ai->stream_finished) {
      55           8 :                 gf_sc_invalidate(ai->compositor, NULL);
      56             :         }
      57             : 
      58             :         /*no more data or not enough data, reset syncro drift*/
      59       39475 :         if (!frame) {
      60       28027 :                 if (!ai->stream_finished && gf_mo_is_started(ai->stream) && (ai->stream->odm->ck->speed == FIX_ONE)) {
      61       17826 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_AUDIO, ("[Audio Input] No data in audio object\n"));
      62             :                 }
      63       28027 :                 gf_mo_adjust_clock(ai->stream, 0);
      64       28027 :                 ai->input_ifce.is_buffering = gf_mo_is_buffering(ai->stream);
      65       28027 :                 *size = 0;
      66       28027 :                 return NULL;
      67             :         }
      68       11448 :         ai->need_release = GF_TRUE;
      69             : 
      70             :         //step mode, return the frame without sync check
      71       11448 :         if (ai->compositor->audio_renderer->non_rt_output) {
      72       11448 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_AUDIO, ("[Audio Input] audio frame CTS %u %d bytes fetched\n", ts, *size));
      73             :                 return frame;
      74             :         }
      75             : 
      76           0 :         speed = gf_mo_get_current_speed(ai->stream);
      77             : 
      78           0 :         gf_mo_get_object_time(ai->stream, &obj_time);
      79           0 :         obj_time += audio_delay_ms;
      80           0 :         if (ai->compositor->bench_mode) {
      81             :                 drift = 0;
      82             :         } else {
      83           0 :                 drift = (s32)obj_time;
      84           0 :                 drift -= (s32)ts;
      85             :         }
      86           0 :         if (ai->stream->odm->prev_clock_at_discontinuity_plus_one) {
      87             :                 s32 drift_old = drift;
      88             :                 s32 diff;
      89           0 :                 drift_old -= (s32) ai->stream->odm->ck->init_timestamp;
      90           0 :                 drift_old += (s32) ai->stream->odm->prev_clock_at_discontinuity_plus_one - 1;
      91           0 :                 diff = ABS(drift_old);
      92           0 :                 diff -= ABS(drift);
      93           0 :                 if (diff < 0) {
      94           0 :                         GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[Audio Input] in clock discontinuity: drift old clock %d new clock %d - disabling clock adjustment\n", drift_old, drift));
      95             :                         drift = 0;
      96             :                         audio_delay_ms = 0;
      97             :                 } else {
      98           0 :                         GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[Audio Input] end of clock discontinuity: drift old clock %d new clock %d\n", drift_old, drift));
      99           0 :                         ai->stream->odm->prev_clock_at_discontinuity_plus_one = 0;
     100           0 :                         if (drift<0) {
     101             :                                 drift = 0;
     102             :                         }
     103             :                 }
     104             :         }
     105             : 
     106             : #ifdef ENABLE_EARLY_FRAME_DETECTION
     107             :         /*too early (silence insertions), skip*/
     108           0 :         if (drift < 0) {
     109           0 :                 GF_LOG(GF_LOG_INFO, GF_LOG_AUDIO, ("[Audio Input] audio too early of %d (CTS %u at OTB %u with audio delay %d ms)\n", drift + audio_delay_ms, ts, obj_time, audio_delay_ms));
     110           0 :                 ai->need_release = GF_FALSE;
     111           0 :                 gf_mo_release_data(ai->stream, 0, -1);
     112           0 :                 *size = 0;
     113           0 :                 return NULL;
     114             :         }
     115             : #endif
     116             :         /*adjust drift*/
     117           0 :         if (audio_delay_ms) {
     118           0 :                 s32 resync_delay = speed > 0 ? FIX2INT(speed * MAX_RESYNC_TIME) : FIX2INT(-speed * MAX_RESYNC_TIME);
     119             :                 /*CU is way too late, discard and fetch a new one - this usually happen when media speed is more than 1*/
     120           0 :                 if (drift>resync_delay) {
     121           0 :                         GF_LOG(GF_LOG_INFO, GF_LOG_AUDIO, ("[Audio Input] Audio data too late obj time %d - CTS %d - drift %d ms - resync forced\n", obj_time - audio_delay_ms, ts, drift));
     122           0 :                         gf_mo_release_data(ai->stream, *size, 2);
     123           0 :                         ai->need_release = GF_FALSE;
     124           0 :                         return gf_audio_input_fetch_frame(callback, size, planar_size, audio_delay_ms);
     125             :                 }
     126           0 :                 if (ai->stream->odm && ai->stream->odm->ck)
     127           0 :                         resync_delay = ai->stream->odm->ck->audio_delay - drift;
     128             :                 else
     129           0 :                         resync_delay = -drift;
     130             :                         
     131           0 :                 if (resync_delay < 0) resync_delay = -resync_delay;
     132             : 
     133           0 :                 if (resync_delay > MIN_DRIFT_ADJUST) {
     134           0 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_AUDIO, ("[Audio Input] Audio clock: delay %d - obj time %d - audio delay %d - CTS %d - adjust drift %d\n", audio_delay_ms, obj_time, audio_delay_ms, ts, drift));
     135           0 :                         gf_mo_adjust_clock(ai->stream, drift);
     136             :                 }
     137             :         }
     138             :         return frame;
     139             : }
     140             : 
     141       11448 : static void gf_audio_input_release_frame(void *callback, u32 nb_bytes)
     142             : {
     143             :         GF_AudioInput *ai = (GF_AudioInput *) callback;
     144       11448 :         if (!ai->stream) return;
     145       11448 :         gf_mo_release_data(ai->stream, nb_bytes, 1);
     146       11448 :         ai->need_release = GF_FALSE;
     147             : }
     148             : 
     149       36543 : static Fixed gf_audio_input_get_speed(void *callback)
     150             : {
     151             :         GF_AudioInput *ai = (GF_AudioInput *) callback;
     152       36543 :         return gf_mo_get_current_speed(ai->stream);
     153             : }
     154             : 
     155       34018 : static Bool gf_audio_input_get_volume(void *callback, Fixed *vol)
     156             : {
     157             :         GF_AudioInput *ai = (GF_AudioInput *) callback;
     158       34018 :         if (ai->snd && ai->snd->GetChannelVolume) {
     159       33869 :                 return ai->snd->GetChannelVolume(ai->snd->owner, vol);
     160             :         } else {
     161             :                 u32 i;
     162        3576 :                 for (i=0; i<GF_AUDIO_MIXER_MAX_CHANNELS; i++)
     163        3576 :                         vol[i] = ai->intensity;
     164             :                         
     165         149 :                 return (ai->intensity==FIX_ONE) ? GF_FALSE : GF_TRUE;
     166             :         }
     167             : }
     168             : 
     169      102433 : static Bool gf_audio_input_is_muted(void *callback)
     170             : {
     171             :         GF_AudioInput *ai = (GF_AudioInput *) callback;
     172      102433 :         if (!ai->stream) return GF_TRUE;
     173             : 
     174      102433 :         if (ai->stream->odm->nb_buffering)
     175       13619 :                 gf_odm_check_buffering(ai->stream->odm, NULL);
     176      102433 :         if (ai->is_muted)
     177             :                 return GF_TRUE;
     178      102433 :         return gf_mo_is_muted(ai->stream);
     179             : }
     180             : 
     181       68576 : static Bool gf_audio_input_get_config(GF_AudioInterface *aifc, Bool for_recf)
     182             : {
     183       68576 :         GF_AudioInput *ai = (GF_AudioInput *) aifc->callback;
     184       68576 :         if (!ai->stream) return GF_FALSE;
     185             :         /*watchout for object reuse*/
     186       68507 :         if (aifc->samplerate &&  !ai->stream->config_changed) return GF_TRUE;
     187             : 
     188          77 :         gf_mo_get_audio_info(ai->stream, &aifc->samplerate, &aifc->afmt , &aifc->chan, &aifc->ch_layout, &aifc->forced_layout);
     189             : 
     190          77 :         if (!for_recf)
     191           0 :                 return aifc->samplerate ? GF_TRUE : GF_FALSE;
     192             : 
     193          77 :         if (aifc->samplerate && aifc->chan && aifc->afmt && ((aifc->chan<=2) || aifc->ch_layout))  {
     194          21 :                 ai->stream->config_changed = GF_FALSE;
     195          21 :                 return GF_TRUE;
     196             :         }
     197             :         //still not ready !
     198          56 :         ai->stream->config_changed=GF_TRUE;
     199          56 :         return GF_FALSE;
     200             : }
     201             : 
     202             : GF_EXPORT
     203         236 : void gf_sc_audio_setup(GF_AudioInput *ai, GF_Compositor *compositor, GF_Node *node)
     204             : {
     205             :         memset(ai, 0, sizeof(GF_AudioInput));
     206         236 :         ai->owner = node;
     207         236 :         ai->compositor = compositor;
     208         236 :         ai->stream = NULL;
     209             :         /*setup io interface*/
     210         236 :         ai->input_ifce.FetchFrame = gf_audio_input_fetch_frame;
     211         236 :         ai->input_ifce.ReleaseFrame = gf_audio_input_release_frame;
     212         236 :         ai->input_ifce.GetConfig = gf_audio_input_get_config;
     213         236 :         ai->input_ifce.GetChannelVolume = gf_audio_input_get_volume;
     214         236 :         ai->input_ifce.GetSpeed = gf_audio_input_get_speed;
     215         236 :         ai->input_ifce.IsMuted = gf_audio_input_is_muted;
     216         236 :         ai->input_ifce.callback = ai;
     217         236 :         ai->intensity = FIX_ONE;
     218             : 
     219         236 :         ai->speed = FIX_ONE;
     220             : 
     221         236 : }
     222             : 
     223         236 : void gf_sc_audio_predestroy(GF_AudioInput *ai)
     224             : {
     225         236 :         gf_sc_audio_stop(ai);
     226         236 :         gf_sc_audio_unregister(ai);
     227         236 : }
     228             : 
     229             : GF_EXPORT
     230         566 : GF_Err gf_sc_audio_open(GF_AudioInput *ai, MFURL *url, Double clipBegin, Double clipEnd, Bool lock_timeline)
     231             : {
     232         566 :         if (ai->is_open) return GF_BAD_PARAM;
     233             : 
     234             :         /*get media object*/
     235         566 :         ai->stream = gf_mo_register(ai->owner, url, lock_timeline, GF_FALSE);
     236             :         /*bad URL*/
     237         566 :         if (!ai->stream) return GF_NOT_SUPPORTED;
     238             : 
     239             :         /*request play*/
     240          22 :         gf_mo_play(ai->stream, clipBegin, clipEnd, GF_FALSE);
     241             : 
     242          22 :         ai->stream_finished = GF_FALSE;
     243          22 :         ai->is_open = 1;
     244             :         //force reload of audio props
     245          22 :         ai->stream->config_changed = GF_TRUE;
     246             : 
     247          22 :         return GF_OK;
     248             : }
     249             : 
     250             : GF_EXPORT
     251         240 : void gf_sc_audio_stop(GF_AudioInput *ai)
     252             : {
     253         240 :         if (!ai->is_open) return;
     254             : 
     255             :         /*we must make sure audio mixer is not using the stream otherwise we may leave it dirty (with unrelease frame)*/
     256          22 :         gf_mixer_lock(ai->compositor->audio_renderer->mixer, GF_TRUE);
     257             : 
     258             :         assert(!ai->need_release);
     259             : 
     260          22 :         gf_mo_stop(&ai->stream);
     261          22 :         ai->is_open = 0;
     262          22 :         gf_mo_unregister(ai->owner, ai->stream);
     263          22 :         ai->stream = NULL;
     264             : 
     265          22 :         gf_mixer_lock(ai->compositor->audio_renderer->mixer, GF_FALSE);
     266             : 
     267             : }
     268             : 
     269             : GF_EXPORT
     270           6 : void gf_sc_audio_restart(GF_AudioInput *ai)
     271             : {
     272           6 :         if (!ai->is_open) return;
     273           6 :         if (ai->need_release) gf_mo_release_data(ai->stream, 0xFFFFFFFF, 2);
     274           6 :         ai->need_release = GF_FALSE;
     275           6 :         ai->stream_finished = GF_FALSE;
     276             : 
     277           6 :         gf_mo_restart(ai->stream);
     278             : }
     279             : 
     280             : GF_EXPORT
     281           9 : Bool gf_sc_audio_check_url(GF_AudioInput *ai, MFURL *url)
     282             : {
     283           9 :         if (!ai->stream) return url->count;
     284           6 :         return gf_mo_url_changed(ai->stream, url);
     285             : }
     286             : 
     287             : GF_EXPORT
     288        1710 : void gf_sc_audio_register(GF_AudioInput *ai, GF_TraverseState *tr_state)
     289             : {
     290             :         GF_AudioInterface *aifce;
     291             :         /*check interface is valid*/
     292        1710 :         if (!ai->input_ifce.FetchFrame
     293        1710 :                 || !ai->input_ifce.GetChannelVolume
     294        1710 :                 || !ai->input_ifce.GetConfig
     295        1710 :                 || !ai->input_ifce.GetSpeed
     296        1710 :                 || !ai->input_ifce.IsMuted
     297        1710 :                 || !ai->input_ifce.ReleaseFrame
     298             :            ) return;
     299             : 
     300        1710 :         aifce = &ai->input_ifce;
     301             : 
     302        1710 :         if (tr_state->audio_parent) {
     303             :                 /*this assume only one parent may use an audio node*/
     304          16 :                 if (ai->register_with_parent) return;
     305           1 :                 if (ai->register_with_renderer) {
     306           0 :                         gf_sc_ar_remove_src(ai->compositor->audio_renderer, aifce);
     307           0 :                         ai->register_with_renderer = GF_FALSE;
     308             :                 }
     309           1 :                 tr_state->audio_parent->add_source(tr_state->audio_parent, ai);
     310           1 :                 ai->register_with_parent = GF_TRUE;
     311           1 :                 ai->snd = tr_state->sound_holder;
     312        1694 :         } else if (!ai->register_with_renderer) {
     313             : 
     314          21 :                 if (ai->register_with_parent) {
     315           0 :                         ai->register_with_parent = GF_FALSE;
     316             :                         /*if used in a parent audio group, do a complete traverse to rebuild the group*/
     317           0 :                         gf_sc_invalidate(ai->compositor, NULL);
     318             :                 }
     319             : 
     320          21 :                 gf_sc_ar_add_src(ai->compositor->audio_renderer, aifce);
     321          21 :                 ai->register_with_renderer = GF_TRUE;
     322          21 :                 ai->snd = tr_state->sound_holder;
     323             :         }
     324             : }
     325             : 
     326             : GF_EXPORT
     327         243 : void gf_sc_audio_unregister(GF_AudioInput *ai)
     328             : {
     329         243 :         GF_AudioInterface *aifce = &ai->input_ifce;
     330             : 
     331         243 :         if (ai->register_with_renderer) {
     332          21 :                 ai->register_with_renderer = GF_FALSE;
     333          21 :                 gf_sc_ar_remove_src(ai->compositor->audio_renderer, aifce);
     334             :         } else {
     335             :                 /*if used in a parent audio group, do a complete traverse to rebuild the group*/
     336         222 :                 gf_sc_invalidate(ai->compositor, NULL);
     337             :         }
     338         243 : }
     339             : 

Generated by: LCOV version 1.13