LCOV - code coverage report
Current view: top level - filters - out_audio.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 224 321 69.8 %
Date: 2021-04-29 23:48:07 Functions: 9 11 81.8 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2018-2021
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / audio output 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             : 
      27             : #include <gpac/filters.h>
      28             : #include <gpac/constants.h>
      29             : #include <gpac/modules/audio_out.h>
      30             : #include <gpac/thread.h>
      31             : 
      32             : typedef struct
      33             : {
      34             :         //options
      35             :         char *drv;
      36             :         u32 bnum, bdur, threaded, priority;
      37             :         Bool clock;
      38             :         GF_Fraction64 dur;
      39             :         Double speed, start;
      40             :         u32 vol, pan, buffer, mbuffer, rbuffer;
      41             :         GF_Fraction adelay;
      42             :         
      43             :         GF_FilterPid *pid;
      44             :         u32 sr, afmt, nb_ch, timescale;
      45             :         u64 ch_cfg;
      46             :         GF_AudioOutput *audio_out;
      47             :         GF_Thread *th;
      48             :         u32 audio_th_state;
      49             :         Bool needs_recfg, wait_recfg;
      50             :         u32 bytes_per_sample;
      51             : 
      52             :         u32 pck_offset;
      53             :         u64 first_cts;
      54             :         Bool aborted;
      55             :         u32 speed_set;
      56             :         GF_Filter *filter;
      57             :         Bool is_eos, in_error;
      58             :         Bool first_write_done;
      59             : 
      60             :         s64 pid_delay;
      61             : 
      62             :         Bool buffer_done, no_buffering;
      63             :         u64 hwdelay_us, totaldelay_us;
      64             : 
      65             :         u64 rebuffer;
      66             :         Bool do_seek;
      67             : } GF_AudioOutCtx;
      68             : 
      69             : 
      70          10 : void aout_reconfig(GF_AudioOutCtx *ctx)
      71             : {
      72             :         u32 sr, afmt, old_afmt, nb_ch;
      73             :         u64 ch_cfg;
      74             :         GF_Err e = GF_OK;
      75          10 :         sr = ctx->sr;
      76             : 
      77          10 :         nb_ch = ctx->nb_ch;
      78          10 :         afmt = old_afmt = ctx->afmt;
      79          10 :         ch_cfg = ctx->ch_cfg;
      80             : 
      81             :         //config not ready, wait
      82          10 :         if (!nb_ch || !sr || !afmt) {
      83             :                 //force a get_packet to trigger reconfigure
      84           0 :                 gf_filter_pid_get_packet(ctx->pid);
      85           0 :                 return;
      86             :         }
      87             :         //we only support packed audio at output
      88          10 :         if (afmt>GF_AUDIO_FMT_LAST_PACKED)
      89           1 :                 afmt -= GF_AUDIO_FMT_LAST_PACKED;
      90             : 
      91          10 :         e = ctx->audio_out->Configure(ctx->audio_out, &sr, &nb_ch, &afmt, ch_cfg);
      92          10 :         if (e) {
      93           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[AudioOut] Failed to configure audio output: %s\n", gf_error_to_string(e) ));
      94           0 :                 afmt = GF_AUDIO_FMT_S16;
      95           0 :                 sr = 44100;
      96           0 :                 nb_ch = 2;
      97             :         }
      98          10 :         if (ctx->speed == FIX_ONE) ctx->speed_set = 1;
      99             : 
     100          10 :         if (ctx->vol<=100) {
     101           8 :                 if (ctx->audio_out->SetVolume)
     102           8 :                         ctx->audio_out->SetVolume(ctx->audio_out, ctx->vol);
     103           8 :                 ctx->vol = 101;
     104             :         }
     105          10 :         if (ctx->pan<=100) {
     106           8 :                 if (ctx->audio_out->SetPan)
     107           8 :                         ctx->audio_out->SetPan(ctx->audio_out, ctx->pan);
     108           8 :                 ctx->pan = 101;
     109             :         }
     110             : 
     111             : 
     112          10 :         if (ctx->sr * ctx->nb_ch * old_afmt == 0) {
     113           0 :                 ctx->needs_recfg = GF_FALSE;
     114           0 :                 ctx->wait_recfg = GF_FALSE;
     115           0 :                 return;
     116             :         }
     117             : 
     118          10 :         if ((sr != ctx->sr) || (nb_ch!=ctx->nb_ch) || (afmt!=old_afmt) || !ctx->speed_set) {
     119           3 :                 gf_filter_pid_negociate_property(ctx->pid, GF_PROP_PID_SAMPLE_RATE, &PROP_UINT(sr));
     120           3 :                 gf_filter_pid_negociate_property(ctx->pid, GF_PROP_PID_AUDIO_FORMAT, &PROP_UINT(afmt));
     121           3 :                 gf_filter_pid_negociate_property(ctx->pid, GF_PROP_PID_NUM_CHANNELS, &PROP_UINT(nb_ch));
     122           3 :                 gf_filter_pid_negociate_property(ctx->pid, GF_PROP_PID_AUDIO_SPEED, &PROP_DOUBLE(ctx->speed));
     123           3 :                 ctx->speed_set = (ctx->speed==1.0) ? 1 : 2;
     124           3 :                 ctx->needs_recfg = GF_FALSE;
     125             :                 //drop all packets until next reconfig
     126           3 :                 ctx->wait_recfg = GF_TRUE;
     127           3 :                 ctx->sr = sr;
     128           3 :                 ctx->nb_ch = nb_ch;
     129           3 :                 ctx->afmt = afmt;
     130           3 :                 ctx->ch_cfg = ch_cfg;
     131           7 :         } else if (e==GF_OK) {
     132           7 :                 ctx->needs_recfg = GF_FALSE;
     133           7 :                 ctx->wait_recfg = GF_FALSE;
     134             :         } else {
     135           0 :                 if (!ctx->in_error) {
     136           0 :                         ctx->in_error = GF_TRUE;
     137           0 :                         gf_filter_abort(ctx->filter);
     138             :                 }
     139             :                 return;
     140             :         }
     141          10 :         ctx->bytes_per_sample = gf_audio_fmt_bit_depth(afmt) * nb_ch / 8;
     142          10 :         ctx->hwdelay_us = 0;
     143          10 :         if (ctx->audio_out->GetAudioDelay) {
     144          10 :                 ctx->hwdelay_us = ctx->audio_out->GetAudioDelay(ctx->audio_out);
     145          10 :                 ctx->hwdelay_us *= 1000;
     146          10 :                 GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[AudioOut] Hardware delay is "LLU" us\n", ctx->hwdelay_us ));
     147             :         }
     148          10 :         ctx->totaldelay_us = 0;
     149          10 :         if (ctx->audio_out->GetTotalBufferTime) {
     150          10 :                 ctx->totaldelay_us = ctx->audio_out->GetTotalBufferTime(ctx->audio_out);
     151          10 :                 ctx->totaldelay_us *= 1000;
     152          10 :                 GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[AudioOut] Total audio delay is "LLU" ms\n", ctx->totaldelay_us ));
     153             :         }
     154             : }
     155             : 
     156           0 : u32 aout_th_proc(void *p)
     157             : {
     158             :         GF_AudioOutCtx *ctx = (GF_AudioOutCtx *) p;
     159             : 
     160           0 :         ctx->audio_th_state = 1;
     161             : 
     162           0 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[AudioOut] Entering audio thread ID %d\n", gf_th_id() ));
     163             : 
     164           0 :         while (ctx->audio_th_state == 1) {
     165           0 :                 if (ctx->needs_recfg) {
     166           0 :                         aout_reconfig(ctx);
     167           0 :                 } else if (ctx->pid) {
     168           0 :                         ctx->audio_out->WriteAudio(ctx->audio_out);
     169             :                 } else {
     170           0 :                         gf_sleep(10);
     171             :                 }
     172             :         }
     173           0 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[AudioOut] Exiting audio thread\n"));
     174           0 :         ctx->audio_out->Shutdown(ctx->audio_out);
     175           0 :         ctx->audio_th_state = 3;
     176           0 :         return 0;
     177             : }
     178             : 
     179             : 
     180    36109682 : static u32 aout_fill_output(void *ptr, u8 *buffer, u32 buffer_size)
     181             : {
     182             :         u32 done = 0;
     183             :         GF_AudioOutCtx *ctx = ptr;
     184             :         Bool is_first_pck = GF_TRUE;
     185             : 
     186    36109682 :         memset(buffer, 0, buffer_size);
     187    36109682 :         if (!ctx->pid || ctx->aborted) return 0;
     188    36109681 :         if (!ctx->speed) return 0;
     189             : 
     190    36109682 :         if (ctx->do_seek) {
     191             :                 GF_FilterEvent evt;
     192             : 
     193           0 :                 GF_FEVT_INIT(evt, GF_FEVT_STOP, ctx->pid);
     194           0 :                 gf_filter_pid_send_event(ctx->pid, &evt);
     195             : 
     196           0 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[AudioOut] Seek request to %f speed %f\n", ctx->start, ctx->speed));
     197           0 :                 gf_filter_pid_init_play_event(ctx->pid, &evt, ctx->start, ctx->speed, "VideoOut");
     198           0 :                 gf_filter_pid_send_event(ctx->pid, &evt);
     199             : 
     200             :                 //reinit clock
     201           0 :                 ctx->first_write_done = GF_FALSE;
     202           0 :                 ctx->buffer_done = GF_FALSE;
     203           0 :                 ctx->do_seek = GF_FALSE;
     204           0 :                 ctx->pck_offset = 0;
     205             :                 return 0;
     206             :         }
     207             : 
     208             : 
     209    36109682 :         if (!ctx->buffer_done) {
     210             :                 u32 size;
     211             :                 GF_FilterPacket *pck;
     212             : 
     213             :                 //query full buffer duration in us
     214    34336850 :                 u64 dur = gf_filter_pid_query_buffer_duration(ctx->pid, GF_FALSE);
     215             : 
     216    34336849 :                 GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[AudioOut] buffer %d / %d ms\r", dur/1000, ctx->buffer));
     217             : 
     218             :                 /*the compositor sends empty packets after its reconfiguration to check when the config is active
     219             :                 we therefore probe the first packet before probing the buffer fullness*/
     220    34336849 :                 pck = gf_filter_pid_get_packet(ctx->pid);
     221    68673695 :                 if (!pck) return 0;
     222             : 
     223         469 :                 if (! gf_filter_pck_is_blocking_ref(pck)) {
     224         469 :                         if ((dur < ctx->buffer * 1000) && !gf_filter_pid_is_eos(ctx->pid))
     225             :                                 return 0;
     226           3 :                         gf_filter_pck_get_data(pck, &size);
     227           3 :                         if (!size) {
     228           0 :                                 gf_filter_pid_drop_packet(ctx->pid);
     229           0 :                                 return 0;
     230             :                         }
     231             :                         //check the decoder output is full (avoids initial underrun)
     232           3 :                         if (gf_filter_pid_query_buffer_duration(ctx->pid, GF_TRUE)==0)
     233             :                                 return 0;
     234             :                 }
     235           3 :                 ctx->buffer_done = GF_TRUE;
     236           3 :                 if (ctx->rebuffer) {
     237           0 :                         GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[AudioOut] rebuffer done in "LLU" ms\n", (u32) ( (gf_sys_clock_high_res() - ctx->rebuffer) /1000) ));
     238           0 :                         ctx->rebuffer = 0;
     239             :                 }
     240     1772832 :         } else if (ctx->rbuffer && !ctx->rebuffer) {
     241             :                 //query full buffer duration in us
     242           0 :                 u64 dur = gf_filter_pid_query_buffer_duration(ctx->pid, GF_FALSE);
     243           0 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[AudioOut] buffer %d / %d ms\r", dur/1000, ctx->buffer));
     244           0 :                 if ((dur < ctx->rbuffer * 1000) && !gf_filter_pid_has_seen_eos(ctx->pid)) {
     245           0 :                         GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[AudioOut] buffer %u less than min threshold %u, rebuffering\n", (u32) (dur/1000), ctx->rbuffer));
     246           0 :                         ctx->rebuffer = gf_sys_clock_high_res();
     247           0 :                         ctx->buffer_done = GF_FALSE;
     248           0 :                         return GF_OK;
     249             :                 }
     250             : #ifndef GPAC_DISABLE_LOG
     251     1772832 :         } else if (gf_log_tool_level_on(GF_LOG_MMIO, GF_LOG_DEBUG)) {
     252       45026 :                 u64 dur = gf_filter_pid_query_buffer_duration(ctx->pid, GF_FALSE);
     253       45026 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[AudioOut] buffer %d / %d ms\r", dur/1000, ctx->buffer));
     254             : #endif
     255             :         }
     256             : 
     257             :         //do not throw underflow log util first packet is fetched
     258     1772835 :         if (ctx->first_write_done)
     259             :                 is_first_pck = GF_FALSE;
     260             : 
     261     1772859 :         while (done < buffer_size) {
     262             :                 const char *data;
     263             :                 u32 size;
     264             :                 u64 cts;
     265             :                 s64 delay;
     266     1772859 :                 GF_FilterPacket *pck = gf_filter_pid_get_packet(ctx->pid);
     267     1772859 :                 if (!pck) {
     268     1032914 :                         if (gf_filter_pid_is_eos(ctx->pid)) {
     269      534142 :                                 ctx->is_eos = GF_TRUE;
     270      498772 :                         } else if (!is_first_pck) {
     271      498731 :                                 GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[AudioOut] buffer underflow\n"));
     272             :                         }
     273     1772835 :                         return done;
     274             :                 }
     275      739945 :                 ctx->is_eos = GF_FALSE;
     276      739945 :                 if (ctx->needs_recfg) {
     277             :                         return done;
     278             :                 }
     279             : 
     280          24 :                 delay = ctx->pid_delay;
     281          24 :                 if (ctx->adelay.den)
     282          24 :                         delay += ctx->adelay.num * (s32)ctx->timescale / (s32)ctx->adelay.den;
     283             : 
     284          24 :                 cts = gf_filter_pck_get_cts(pck);
     285          24 :                 if (delay >= 0) {
     286          24 :                         cts += delay;
     287           0 :                 } else if (cts < (u64) -delay) {
     288           0 :                         gf_filter_pid_drop_packet(ctx->pid);
     289           0 :                         continue;
     290             :                 } else {
     291           0 :                         cts -= (u64) -delay;
     292             :                 }
     293             : 
     294          24 :                 if (ctx->dur.num>0) {
     295          24 :                         if (!ctx->first_cts) ctx->first_cts = cts+1;
     296             : 
     297          24 :                         if ((cts - ctx->first_cts + 1) * ctx->dur.den > (u64) ctx->dur.num*ctx->timescale) {
     298           0 :                                 gf_filter_pid_drop_packet(ctx->pid);
     299           0 :                                 if (!ctx->aborted) {
     300             :                                         GF_FilterEvent evt;
     301           0 :                                         GF_FEVT_INIT(evt, GF_FEVT_STOP, ctx->pid);
     302           0 :                                         gf_filter_pid_send_event(ctx->pid, &evt);
     303             : 
     304           0 :                                         ctx->aborted = GF_TRUE;
     305             :                                 }
     306             :                                 return done;
     307             :                         }
     308             :                 }
     309             : 
     310          24 :                 data = gf_filter_pck_get_data(pck, &size);
     311             : 
     312          24 :                 if (!done && ctx->clock && data && size) {
     313             :                         GF_Fraction64 timestamp;
     314          24 :                         timestamp.num = cts;
     315          24 :                         if (ctx->pck_offset)
     316           0 :                                 timestamp.num += ctx->pck_offset/ctx->bytes_per_sample;
     317             : 
     318          24 :                         timestamp.num -= (ctx->hwdelay_us*ctx->timescale)/1000000;
     319          24 :                         if (timestamp.num<0) timestamp.num = 0;
     320             :                         timestamp.den = ctx->timescale;
     321          24 :                         gf_filter_hint_single_clock(ctx->filter, gf_sys_clock_high_res(), timestamp);
     322          24 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[AudioOut] At %d ms audio frame CTS "LLU" (compensated time %g s)\n", gf_sys_clock(), cts, ((Double)timestamp.num)/timestamp.den ));
     323             :                 }
     324             :                 
     325          24 :                 if (data && !ctx->wait_recfg) {
     326             :                         u32 nb_copy;
     327             :                         assert(size >= ctx->pck_offset);
     328             :                         
     329          24 :                         nb_copy = (size - ctx->pck_offset);
     330          24 :                         if (nb_copy + done > buffer_size) nb_copy = buffer_size - done;
     331          24 :                         memcpy(buffer+done, data+ctx->pck_offset, nb_copy);
     332             : 
     333          24 :                         if (!done && gf_filter_reporting_enabled(ctx->filter)) {
     334             :                                 char szStatus[1024];
     335           0 :                                 u64 bdur = gf_filter_pid_query_buffer_duration(ctx->pid, GF_FALSE);
     336           0 :                                 sprintf(szStatus, "%d Hz %d ch %s buffer %d / %d ms", ctx->sr, ctx->nb_ch, gf_audio_fmt_name(ctx->afmt), (u32) (bdur/1000), ctx->buffer);
     337           0 :                                 gf_filter_update_status(ctx->filter, -1, szStatus);
     338             :                         }
     339             : 
     340             : 
     341          24 :                         done += nb_copy;
     342          24 :                         ctx->first_write_done = GF_TRUE;
     343             :                         is_first_pck = GF_FALSE;
     344          24 :                         if (nb_copy + ctx->pck_offset < size) {
     345           0 :                                 ctx->pck_offset += nb_copy;
     346           0 :                                 return done;
     347             :                         }
     348          24 :                         ctx->pck_offset = 0;
     349             :                 }
     350          24 :                 gf_filter_pid_drop_packet(ctx->pid);
     351             :         }
     352             :         return done;
     353             : }
     354             : 
     355           8 : void aout_set_priority(GF_AudioOutCtx *ctx, u32 prio)
     356             : {
     357           8 :         if (prio==ctx->priority) return;
     358           8 :         ctx->priority = prio;
     359           8 :         if (ctx->th) gf_th_set_priority(ctx->th, (s32) ctx->priority);
     360           8 :         else if (ctx->audio_out->SelfThreaded && ctx->audio_out->SetPriority)
     361           8 :                 ctx->audio_out->SetPriority(ctx->audio_out, ctx->priority);
     362             : }
     363             : 
     364          11 : static GF_Err aout_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
     365             : {
     366             :         const GF_PropertyValue *p;
     367          11 :         GF_PropertyEntry *pe=NULL;
     368             :         u32 sr, nb_ch, afmt, timescale;
     369             :         u64 ch_cfg;
     370          11 :         GF_AudioOutCtx *ctx = (GF_AudioOutCtx *) gf_filter_get_udta(filter);
     371             : 
     372          11 :         if (is_remove) {
     373             :                 assert(ctx->pid==pid);
     374           0 :                 ctx->pid=NULL;
     375           0 :                 return GF_OK;
     376             :         }
     377             :         assert(!ctx->pid || (ctx->pid==pid));
     378             : 
     379          11 :         if (!gf_filter_pid_check_caps(pid))
     380             :                 return GF_NOT_SUPPORTED;
     381             : 
     382             :         sr = afmt = nb_ch = timescale = 0;
     383             :         ch_cfg = 0;
     384          11 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_TIMESCALE);
     385          11 :         if (p) timescale = p->value.uint;
     386             : 
     387          11 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAMPLE_RATE);
     388          11 :         if (p) sr = p->value.uint;
     389          11 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_FORMAT);
     390          11 :         if (p) afmt = p->value.uint;
     391          11 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_NUM_CHANNELS);
     392          11 :         if (p) nb_ch = p->value.uint;
     393          11 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_CHANNEL_LAYOUT);
     394          11 :         if (p) ch_cfg = p->value.longuint;
     395             : 
     396          11 :         if (ctx->audio_out->SetVolume) {
     397          11 :                 p = gf_filter_pid_get_info(pid, GF_PROP_PID_AUDIO_VOLUME, &pe);
     398          11 :                 if (p) ctx->audio_out->SetVolume(ctx->audio_out, p->value.uint);
     399             :         }
     400          11 :         if (ctx->audio_out->SetPan) {
     401          11 :                 p = gf_filter_pid_get_info(pid, GF_PROP_PID_AUDIO_PAN, &pe);
     402          11 :                 if (p) ctx->audio_out->SetPan(ctx->audio_out, p->value.uint);
     403             :         }
     404          11 :         gf_filter_release_property(pe);
     405             :         
     406          11 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_PRIORITY);
     407          11 :         if (p) aout_set_priority(ctx, p->value.uint);
     408             : 
     409          11 :         if (ctx->first_cts && (ctx->timescale != timescale)) {
     410           0 :                 ctx->first_cts-=1;
     411           0 :                 ctx->first_cts *= timescale;
     412           0 :                 ctx->first_cts /= ctx->timescale;
     413           0 :                 ctx->first_cts+=1;
     414             :         }
     415          11 :         ctx->timescale = timescale;
     416             : 
     417          11 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_PLAY_BUFFER);
     418          11 :         ctx->no_buffering = (p && !p->value.sint) ? GF_TRUE : GF_FALSE;
     419          11 :         if (ctx->no_buffering) ctx->buffer_done = GF_TRUE;
     420             : 
     421          11 :         if ((ctx->sr==sr) && (ctx->afmt == afmt) && (ctx->nb_ch == nb_ch) && (ctx->ch_cfg == ch_cfg)) {
     422           1 :                 ctx->needs_recfg = GF_FALSE;
     423           1 :                 ctx->wait_recfg = GF_FALSE;
     424           1 :                 return GF_OK;
     425             :         }
     426             : 
     427             :         //whenever change of sample rate / format / channel, force buffer requirements and speed setup
     428          10 :         if ((ctx->sr!=sr) || (ctx->afmt != afmt) || (ctx->nb_ch != nb_ch)) {
     429             :                 GF_FilterEvent evt;
     430           8 :                 ctx->speed_set = 0;
     431             : 
     432             :                 //set buffer reqs to bdur or 100 ms - we don't "buffer" in the filter, but this will allow dispatching
     433             :                 //several input frames in the buffer (default being 1 pck / 1000 us max in buffers). Not doing so could cause
     434             :                 //the session to end because input is blocked (no tasks posted) and output still holds a packet 
     435           8 :                 GF_FEVT_INIT(evt, GF_FEVT_BUFFER_REQ, pid);
     436           8 :                 evt.buffer_req.max_buffer_us = ctx->buffer * 1000;
     437           8 :                 if (ctx->bdur) {
     438           8 :                         u64 b = ctx->bdur;
     439           8 :                         b *= 1000;
     440           8 :                         if (evt.buffer_req.max_buffer_us < b)
     441           0 :                                 evt.buffer_req.max_buffer_us = (u32) b;
     442             :                 }
     443             :                 //we have a max buffer, move our computed max to playout and setup max buffer
     444           8 :                 if (ctx->mbuffer > evt.buffer_req.max_buffer_us / 1000 ) {
     445           0 :                         evt.buffer_req.max_playout_us = evt.buffer_req.max_buffer_us;
     446           0 :                         evt.buffer_req.max_buffer_us = ctx->mbuffer * 1000;
     447             :                 }
     448             :                 //we don't have a max buffer, set buffer requirements to PID only
     449             :                 else {
     450           8 :                         evt.buffer_req.pid_only = GF_TRUE;
     451             :                 }
     452             : 
     453           8 :                 gf_filter_pid_send_event(pid, &evt);
     454             : 
     455           8 :                 gf_filter_pid_init_play_event(pid, &evt, ctx->start, ctx->speed, "AudioOut");
     456           8 :                 gf_filter_pid_send_event(pid, &evt);
     457           8 :                 ctx->speed = evt.play.speed;
     458           8 :                 ctx->start = evt.play.start_range;
     459             :         }
     460             : 
     461          10 :         ctx->pid = pid;
     462          10 :         ctx->sr = sr;
     463          10 :         ctx->afmt = afmt;
     464          10 :         ctx->nb_ch = nb_ch;
     465          10 :         ctx->ch_cfg = ch_cfg;
     466             : 
     467          10 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_PRIORITY);
     468          10 :         if (p) aout_set_priority(ctx, p->value.uint);
     469             : 
     470          10 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_DELAY);
     471          10 :         ctx->pid_delay = p ? p->value.longsint : 0;
     472             : 
     473          10 :         ctx->needs_recfg = GF_TRUE;
     474             :         
     475             :         //not threaded, request a task to restart audio (cannot do it during the audio callback)
     476          10 :         if (!ctx->th) gf_filter_post_process_task(filter);
     477             :         return GF_OK;
     478             : }
     479             : 
     480          10 : static GF_Err aout_initialize(GF_Filter *filter)
     481             : {
     482             :         const char *sOpt;
     483             :         void *os_wnd_handler;
     484             :         GF_Err e;
     485          10 :         GF_AudioOutCtx *ctx = (GF_AudioOutCtx *) gf_filter_get_udta(filter);
     486             : 
     487          10 :         ctx->filter = filter;
     488             : 
     489             : 
     490          10 :         ctx->audio_out = (GF_AudioOutput *) gf_module_load(GF_AUDIO_OUTPUT_INTERFACE, ctx->drv);
     491             :         /*if not init we run with a NULL audio compositor*/
     492          10 :         if (!ctx->audio_out) {
     493           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[AudioOut] No audio output modules found, cannot load audio output\n"));
     494             :                 return GF_IO_ERR;
     495             :         }
     496          10 :         if (!gf_opts_get_key("core", "audio-output")) {
     497           0 :                 gf_opts_set_key("core", "audio-output", ctx->audio_out->module_name);
     498             :         }
     499             : 
     500          10 :         ctx->audio_out->FillBuffer = aout_fill_output;
     501          10 :         ctx->audio_out->audio_renderer = ctx;
     502             : 
     503          10 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[AudioOut] Setting up audio module %s\n", ctx->audio_out->module_name));
     504             : 
     505          10 :         if (!ctx->bnum || !ctx->bdur) ctx->bnum = ctx->bdur = 0;
     506             : 
     507          10 :         os_wnd_handler = NULL;
     508          10 :         sOpt = gf_opts_get_key("Temp", "OSWnd");
     509          10 :         if (sOpt) sscanf(sOpt, "%p", &os_wnd_handler);
     510          10 :         e = ctx->audio_out->Setup(ctx->audio_out, os_wnd_handler, ctx->bnum, ctx->bdur);
     511             : 
     512          10 :         if (e != GF_OK) {
     513           2 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[AudioOut] Could not setup module %s\n", ctx->audio_out->module_name));
     514           2 :                 gf_modules_close_interface((GF_BaseInterface *)ctx->audio_out);
     515           2 :                 ctx->audio_out = NULL;
     516           2 :                 return e;
     517             :         }
     518             :         /*only used for coverage for now*/
     519           8 :         if (ctx->audio_out->QueryOutputSampleRate) {
     520           8 :                 u32 sr = 48000;
     521           8 :                 u32 ch = 2;
     522           8 :                 u32 bps = 16;
     523           8 :                 ctx->audio_out->QueryOutputSampleRate(ctx->audio_out, &sr, &ch, &bps);
     524             :         }
     525           8 :         if (ctx->audio_out->SelfThreaded) {
     526           0 :         } else if (ctx->threaded) {
     527           0 :                 ctx->th = gf_th_new("AudioOutput");
     528           0 :                 gf_th_run(ctx->th, aout_th_proc, ctx);
     529             :         }
     530             : 
     531           8 :         aout_set_priority(ctx, GF_THREAD_PRIORITY_REALTIME);
     532             : 
     533           8 :         return GF_OK;
     534             : }
     535             : 
     536           9 : static void aout_finalize(GF_Filter *filter)
     537             : {
     538           9 :         GF_AudioOutCtx *ctx = (GF_AudioOutCtx *) gf_filter_get_udta(filter);
     539             : 
     540             :         /*stop and shutdown*/
     541           9 :         if (ctx->audio_out) {
     542             :                 /*kill audio thread*/
     543           7 :                 if (ctx->th) {
     544           0 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[AudioOut] stopping audio thread\n"));
     545           0 :                         ctx->audio_th_state = 2;
     546           0 :                         while (ctx->audio_th_state != 3) {
     547           0 :                                 gf_sleep(33);
     548             :                         }
     549           0 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[AudioOut] audio thread stopped\n"));
     550           0 :                         gf_th_del(ctx->th);
     551             :                 } else {
     552           7 :                         ctx->audio_out->Shutdown(ctx->audio_out);
     553             :                 }
     554           7 :                 gf_modules_close_interface((GF_BaseInterface *)ctx->audio_out);
     555           7 :                 ctx->audio_out = NULL;
     556             :         }
     557           9 : }
     558             : 
     559         191 : static GF_Err aout_process(GF_Filter *filter)
     560             : {
     561         191 :         GF_AudioOutCtx *ctx = (GF_AudioOutCtx *) gf_filter_get_udta(filter);
     562             : 
     563         191 :         if (ctx->in_error)
     564             :                 return GF_IO_ERR;
     565             : 
     566         191 :         if (!ctx->th && ctx->needs_recfg) {
     567          10 :                 aout_reconfig(ctx);
     568             :         }
     569             : 
     570         191 :         if (ctx->th || ctx->audio_out->SelfThreaded) {
     571         191 :                 if (ctx->is_eos) return GF_EOS;
     572         139 :                 gf_filter_ask_rt_reschedule(filter, 100000);
     573         139 :                 return GF_OK;
     574             :         }
     575             : 
     576           0 :         ctx->audio_out->WriteAudio(ctx->audio_out);
     577           0 :         return GF_OK;
     578             : }
     579             : 
     580      740401 : static Bool aout_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
     581             : {
     582      740401 :         GF_AudioOutCtx *ctx = (GF_AudioOutCtx *) gf_filter_get_udta(filter);
     583      740401 :         if (!ctx->audio_out) return GF_TRUE;
     584             : 
     585      740401 :         switch (evt->base.type) {
     586           2 :         case GF_FEVT_PLAY:
     587           2 :                 if (ctx->audio_out->Play) ctx->audio_out->Play(ctx->audio_out, evt->play.hw_buffer_reset ? 2 : 1);
     588             :                 break;
     589           2 :         case GF_FEVT_STOP:
     590           2 :                 if (ctx->audio_out->Play) ctx->audio_out->Play(ctx->audio_out, 0);
     591             :                 break;
     592             :         default:
     593             :                 break;
     594             :         }
     595             :         //cancel
     596             :         return GF_TRUE;
     597             : }
     598             : 
     599           0 : GF_Err aout_update_arg(GF_Filter *filter, const char *arg_name, const GF_PropertyValue *new_val)
     600             : {
     601           0 :         GF_AudioOutCtx *ctx = (GF_AudioOutCtx *) gf_filter_get_udta(filter);
     602             : 
     603           0 :         if (!strcmp(arg_name, "start")) {
     604           0 :                 if (!ctx->pid) return GF_OK;
     605           0 :                 ctx->do_seek = GF_TRUE;
     606             :         }
     607           0 :         else if (!strcmp(arg_name, "speed")) {
     608           0 :                 if (ctx->speed != new_val->value.number) {
     609           0 :                         if (new_val->value.number==0) return GF_OK;
     610           0 :                         if ((new_val->value.number==1) && ((ctx->speed==0) || (ctx->speed==1)))
     611             :                                 return GF_OK;
     612           0 :                         if (ctx->speed_set != 2) {
     613           0 :                                 ctx->needs_recfg = GF_TRUE;
     614           0 :                                 ctx->speed_set = 0;
     615             :                         } else {
     616             :                                 GF_FilterEvent evt;
     617           0 :                                 GF_FEVT_INIT(evt, GF_FEVT_SET_SPEED, ctx->pid)
     618           0 :                                 evt.play.speed = new_val->value.number;
     619           0 :                                 gf_filter_pid_send_event(ctx->pid, &evt);
     620             :                         }
     621             :                 }
     622             :         }
     623             :         return GF_OK;
     624             : }
     625             : 
     626             : 
     627             : 
     628             : #define OFFS(_n)        #_n, offsetof(GF_AudioOutCtx, _n)
     629             : 
     630             : static const GF_FilterArgs AudioOutArgs[] =
     631             : {
     632             :         { OFFS(drv), "audio driver name", GF_PROP_NAME, NULL, NULL, 0},
     633             :         { OFFS(bnum), "number of audio buffers - 0 for auto", GF_PROP_UINT, "2", NULL, 0},
     634             :         { OFFS(bdur), "total duration of all buffers in ms - 0 for auto. The longer the audio buffer is, the longer the audio latency will be (pause/resume). The quality of fast forward audio playback will also be degradated when using large audio buffers", GF_PROP_UINT, "100", NULL, 0},
     635             :         { OFFS(threaded), "force dedicated thread creation if sound card driver is not threaded", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_ADVANCED},
     636             :         { OFFS(dur), "only play the specified duration", GF_PROP_FRACTION, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
     637             :         { OFFS(clock), "hint audio clock for this stream (reports system time and CTS), for other filters to use", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_ADVANCED},
     638             :         { OFFS(speed), "set playback speed. If speed is negative and start is 0, start is set to -1", GF_PROP_DOUBLE, "1.0", NULL, GF_FS_ARG_UPDATE},
     639             :         { OFFS(start), "set playback start offset. Negative value means percent of media duration with -1 equal to duration", GF_PROP_DOUBLE, "0.0", NULL, GF_FS_ARG_UPDATE},
     640             :         { OFFS(vol), "set default audio volume, as a percentage between 0 and 100", GF_PROP_UINT, "100", "0-100", GF_FS_ARG_UPDATE},
     641             :         { OFFS(pan), "set stereo pan, as a percentage between 0 and 100, 50 being centered", GF_PROP_UINT, "50", "0-100", GF_FS_ARG_UPDATE},
     642             :         { OFFS(buffer), "set playout buffer in ms", GF_PROP_UINT, "200", NULL, 0},
     643             :         { OFFS(mbuffer), "set max buffer occupancy in ms (if less than buffer, use buffer)", GF_PROP_UINT, "0", NULL, 0},
     644             :         { OFFS(rbuffer), "rebuffer trigger in ms (if 0 or more than buffer, disable rebuffering", GF_PROP_UINT, "0", NULL, GF_FS_ARG_UPDATE},
     645             :         { OFFS(adelay), "set audio delay in sec", GF_PROP_FRACTION, "0", NULL, GF_FS_ARG_HINT_ADVANCED|GF_FS_ARG_UPDATE},
     646             :         { OFFS(buffer_done), "buffer done indication (readonly)", GF_PROP_BOOL, NULL, NULL, GF_ARG_HINT_EXPERT},
     647             :         { OFFS(rebuffer), "time at which rebuffer started, 0 if not rebuffering (readonly)", GF_PROP_LUINT, NULL, NULL, GF_ARG_HINT_EXPERT},
     648             :         {0}
     649             : };
     650             : 
     651             : static const GF_FilterCapability AudioOutCaps[] =
     652             : {
     653             :         CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
     654             :         CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_RAW),
     655             :         //we accept all audio formats, but will ask for input reconfiguration if sound card does not support
     656             : };
     657             : 
     658             : 
     659             : GF_FilterRegister AudioOutRegister = {
     660             :         .name = "aout",
     661             :         GF_FS_SET_DESCRIPTION("Audio output")
     662             :         GF_FS_SET_HELP("This filter outputs a single uncompressed audio PID to a sound card or other audio output device.")
     663             :         .private_size = sizeof(GF_AudioOutCtx),
     664             :         .args = AudioOutArgs,
     665             :         SETCAPS(AudioOutCaps),
     666             :         .initialize = aout_initialize,
     667             :         .finalize = aout_finalize,
     668             :         .configure_pid = aout_configure_pid,
     669             :         .process = aout_process,
     670             :         .process_event = aout_process_event,
     671             :         .update_arg = aout_update_arg
     672             : };
     673             : 
     674        2877 : const GF_FilterRegister *aout_register(GF_FilterSession *session)
     675             : {
     676        2877 :         return &AudioOutRegister;
     677             : }
     678             : 

Generated by: LCOV version 1.13