[Freeswitch-users] FreeSWITCH call recording missing header information

Chad Phillips chad at apartmentlines.com
Sun Dec 13 23:52:55 UTC 2020


I have a dialplan setup that records a bridged call in FreeSWITCH,
which sets the 'api_hangup_hook' variable to call a lua script when the
call hangs up, and the called lua script moves the recorded .wav file from
it's temporary location to a final destination.

This works, but recently I noticed that some of my .wav recordings have no
header information, which results in many players choking when trying to
play it.

I eventually figured out that the header information for the .wav file
appears to be written *after* the call is hung up, and file move operation
in the api_hangup_hook moves the file before the header information has
been written. This occurs sporadically.

I've found that introducing a 1 second sleep in my lua script fixes this
race condition, but that seems a bit of an ugly hack.

Curious if anybody else has run into this, and/or can offer me a cleaner
solution than my sleep hack.

I'm attaching a brief summary of the dialplan and lua script for anyone
that wants to see the code.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freeswitch.org/pipermail/freeswitch-users/attachments/20201213/dba06bae/attachment-0001.html>
-------------- next part --------------
    <!-- Sets up channel variables related to doing a session recording.  -->
  <extension name="apartmentlines_operator_recording_setup">
    <condition field="destination_number" expression="^apartmentlines_operator_recording_setup$">
      <action application="set" data="RECORD_TITLE=Recording ${caller_id_number} to customer ${MASTER_ID}"/>
      <action application="set" data="RECORD_COPYRIGHT=(c) ${strftime(%Y)} Cardinal One Ventures, LLC"/>
      <action application="set" data="RECORD_SOFTWARE=FreeSwitch"/>
      <action application="set" data="RECORD_COMMENT=Call center: ${CALL_CENTER} Number: ${DIAL_CALL_CENTER_NUMBER}"/>
      <action application="set" data="RECORD_DATE=${strftime(%Y-%m-%d %H:%M)}"/>
      <action application="set" data="RECORD_STEREO=false"/>
      <action application="set" data="RECORD_ANSWER_REQ=true"/>
      <action application="execute_extension" data="apartmentlines_set_operator_recording_filename"/>
      <action application="set" data="bridge_pre_execute_bleg_app=record_session"/>
      <action application="set" data="bridge_pre_execute_bleg_data=$${AL_RECORDING_TEMP_DIR}/operator_recordings_in_progress/${OPERATOR_RECORDING_FILENAME}"/>
    </condition>
  </extension>

  <!-- Sets a filename for an operator recording. -->
  <extension name="apartmentlines_set_operator_recording_filename">
    <condition field="destination_number" expression="^apartmentlines_set_operator_recording_filename$">
      <action application="set" data="OPERATOR_RECORDING_FILENAME=${MASTER_ID}--${strftime(%Y-%m-%dT%H:%M:%S)}_${caller_id_number}.wav"/>
    </condition>
  </extension>
  
  
  <!-- Briges the caller to a call center. -->
  <extension name="apartmentlines_call_center_outdial">
    <condition field="destination_number" expression="^apartmentlines_call_center_outdial$">

      <!-- ...other call setup ... ->

      <!-- Set up the recording. -->
      <action application="execute_extension" data="apartmentlines_operator_recording_setup"/>

      <!-- Set up the hangup hook. -->
      <action application="set" data="session_in_hangup_hook=true"/>
      <action application="set" data="api_hangup_hook=lua apartmentlines_operator_call.lua hangup ${a_leg_uuid}"/>
      <action application="lua" data="apartmentlines_operator_call.lua insert ${a_leg_uuid}"/>

      <!-- Bridge the call. -->
      <action application="bridge" data="{origination_uuid=${b_leg_uuid}}${DIAL_CALL_CENTER_STRING}"/>

    </condition>
  </extension>
  
  --[[
  Moves a completed operator recording from operator_recordings_in_progress to
  operator_recordings.  This prevents the cron-based script that processes
  these recordings to customer directories from processing recordings in
  progress.
]]
function process_finished_operator_recording()
  local AL_RECORDING_TEMP_DIR = get_value("AL_RECORDING_TEMP_DIR")
  local OPERATOR_RECORDINGS = get_value("OPERATOR_RECORDINGS")
  local operator_recording_filename = get_value("OPERATOR_RECORDING_FILENAME")
  local in_progress_recording =  AL_RECORDING_TEMP_DIR .. "/operator_recordings_in_progress/" .. operator_recording_filename
  local recording =  OPERATOR_RECORDINGS .. "/" .. operator_recording_filename
  if file_exists(in_progress_recording) then
    local sleep_secs = 1
    debug_print(string.format([[Sleeping %d seconds to allow recording to finish]], sleep_secs))
    os.execute(string.format([[sleep %d]], sleep_secs))
    local ok = os.execute(string.format([[mv '%s' '%s']], in_progress_recording, recording))
    if ok then
      debug_print(in_progress_recording .. " -> " .. recording)
--[[
      This can be used to dump recordings to a temp directory for inspection.
      local tmpdir = '/tmp/'
      local ok = os.execute(string.format("cp -a '%s' '%s'", recording, tmpdir))
      if ok then
        debug_print(recording .. " copied to " .. tmpdir)
      end
]]
    end
  end
end


More information about the FreeSWITCH-users mailing list