Giter Club home page Giter Club logo

Comments (6)

waboum avatar waboum commented on May 18, 2024

Hi,
If you wish to record audio, you will need to subscribe to the AudioMediaReceived Event
m_audioSocket.AudioMediaReceived += OnAudioMediaReceived;
This will be raised each time you have a received buffer.

private async void OnAudioMediaReceived(object sender, AudioMediaReceivedEventArgs e)
{
    var buffer = e.Buffer;
    try
    {
        if (m_wavFileWriter != null)
        {
            long length = buffer.Length;
            var retrievedBuffer = new byte[length];
            Marshal.Copy(buffer.Data, retrievedBuffer, 0, (int) length);
            await m_wavFileWriter.EnqueueAsync(retrievedBuffer);
        }
    }
    catch (Exception ex)
    {
        //log exception
    }
    finally
    {
        buffer.Dispose();
    }
}        

you can use this helper class to write to a wav file, this has dependency on the TPL dataflow nuget and System.Threading
-First you need to create the writer

m_wavFileWriter = new WavFileWriter("WavOut" + DateTime.UtcNow.Ticks + ".wav",
                    new WavFileSettings());
               await m_wavFileWriter.InitializeAsync();

-Then, when the app is done recording call shutdown:

await m_wavFileWriter.ShutdownAsync();

This is the code for the helper class

 /// <summary>
    ///  wav file writer, this class will create a wav file
    ///  from the received buffers in the smart agents.
    /// </summary>
    internal class WavFileWriter
    {
        private BufferBlock<byte[]> m_queueBlock;
        private FileStream m_fileStream;
        private readonly CancellationTokenSource m_queueCancellationTokenSource;
        private readonly SemaphoreSlim m_syncLock = new SemaphoreSlim(1);
        private readonly WavFileSettings m_wavFileSettings;
        private long m_riffChunkSizePosition;
        private long m_dataChunkSizePosition;

        public bool IsInitialized { get; private set; }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="wavFileSettings"></param>
        public WavFileWriter(string fileName, WavFileSettings wavFileSettings)
        {
           m_fileStream = new FileStream(fileName, FileMode.OpenOrCreate,
                    FileAccess.ReadWrite,
                    FileShare.None);
            m_queueCancellationTokenSource = new CancellationTokenSource();
            m_queueBlock = new BufferBlock<byte[]>(new DataflowBlockOptions { CancellationToken = m_queueCancellationTokenSource.Token});
            m_wavFileSettings = wavFileSettings;
        }

        private void WriteRiffChunk(BinaryWriter bw)
        {
            bw.Write(0x46464952);	//'RIFF'
            //We remember the riffChunkSizePoistion that we have to update later on stream close
            m_riffChunkSizePosition = bw.BaseStream.Position;	//Filelength - 8 bytes
            bw.Write((int)50);	//a 0sec wav file is atleast 58 bytes
            bw.Write(0x45564157);	//'WAVE'
        }

        private void WriteFmtChunk(BinaryWriter bw)
        {
            bw.Write((int)0x20746D66);	//'fmt '
            bw.Write(16);		//16 bytes of format. We produce no 'extra format info'
            bw.Write(m_wavFileSettings.CompressionCode);		//2bytes
            bw.Write(m_wavFileSettings.NumberOfChannels);	//2bytes
            bw.Write(m_wavFileSettings.SampleRate);			//4bytes
            bw.Write(m_wavFileSettings.AvgBytesPerSecond);	//4bytes
            bw.Write((short)2);					//alignment
            bw.Write((short)16);				//significant bits per sample
        }

        private void WriteFactChunk(BinaryWriter bw)
        {
            bw.Write((int)0x74636166);		//'fact' chunk ID
            bw.Write((int)4);			//4 byte Fact Chunk size
            bw.Write((int)0);			//4 byte chunk data. 
        }

        private void WriteDataChunk(BinaryWriter bw)
        {
            bw.Write(0x61746164);		//'data' chunk ID
            //We remember the dataChunkPosition that we have to update later on stream close
            m_dataChunkSizePosition = bw.BaseStream.Position;
            bw.Write((int)0);				//initially, we have no data, so we set the chunk size to 0
        }

        /// <summary>
        /// Initializes the consumer of the queue to wait for new items and process them if available
        /// </summary>
        /// <returns></returns>
        public async Task InitializeAsync()
        {
            if (!IsInitialized)
            {
                await m_syncLock.WaitAsync();
                if (!IsInitialized)
                {
                    IsInitialized = true;
                   
                    //Initialize the headers
                    Debug.Assert(m_fileStream != null, "m_fileStream != null");
                    BinaryWriter bw = new BinaryWriter(m_fileStream);
                    WriteRiffChunk(bw);
                    WriteFmtChunk(bw);
                    WriteFactChunk(bw);
                    WriteDataChunk(bw);

                    await Task.Factory.StartNew( () =>  DequeueAndProcessAsync());
                }

                m_syncLock.Release();
            }
        }

        /// <summary>
        /// Dequeue and process async workitems
        /// </summary>
        /// <returns></returns>
        internal async Task DequeueAndProcessAsync()
        {
            try
            {
                while (await m_queueBlock.OutputAvailableAsync(m_queueCancellationTokenSource.Token))
                {
                     var buffer = await m_queueBlock.ReceiveAsync(m_queueCancellationTokenSource.Token);

                    if (buffer != null)
                    {
                        await m_fileStream.WriteAsync(buffer, 0, buffer.Length);
                    }

                    this.m_queueCancellationTokenSource.Token.ThrowIfCancellationRequested();
                }
            }
            catch (TaskCanceledException ex)
            {
                Debug.Write(string.Format("The queue processing task has been cancelled. Exception: {0}", ex));
            }
            catch (ObjectDisposedException ex)
            {
                Debug.Write(string.Format("The queue processing task object has been disposed. Exception: {0}", ex));
            }
            catch (Exception ex)
            {
                // Catch all other exceptions and log
                Debug.Write(string.Format("Caught Exception: {0}", ex));

                // Continue processing elements in the queue
                await DequeueAndProcessAsync();
            }
        }

        /// <summary>
        /// Enqueue a waitable work item
        /// </summary>
        /// <param name="buffer"></param>
        /// <returns></returns>
        public async Task EnqueueAsync(byte[] buffer)
        {
            try
            {
                await m_queueBlock.SendAsync(buffer, m_queueCancellationTokenSource.Token);
            }
            catch (TaskCanceledException e)
            {
                if (m_queueBlock != null)
                {
                    m_queueBlock.Complete();
                }

                Debug.Write(string.Format("Cannot enqueue because queuing operation has been cancelled. Exception: {0}", e));
            }
            catch (Exception e)
            {
                Debug.Write(string.Format("Failed to enqueue: {0}", e));
            }
        }

        /// <summary>
        /// ShutDown the queue, this will also cancel current operations
        /// </summary>
        /// <returns></returns>
        public async Task ShutdownAsync()
        {
            if (IsInitialized)
            {
                await m_syncLock.WaitAsync();
                if (IsInitialized)
                {
                    Debug.Assert(m_queueBlock != null);
                    // Allow no more processing on the queue
                    m_queueBlock.Complete();

                    // Cancel the queue task
                    m_queueCancellationTokenSource.Cancel();
                    m_queueCancellationTokenSource.Dispose();
                    IsInitialized = false;
                    CloseFileStream();
                }

                m_syncLock.Release();
            }
        }

        private void CloseFileStream()
        {
            if (m_fileStream != null)
            {
                
                try
                {
                    using(BinaryWriter bw = new BinaryWriter(m_fileStream))
                    {
                       //Lets update the riffChunkSize header value
                       m_fileStream.Seek(m_riffChunkSizePosition, SeekOrigin.Begin);
                       bw.Write((int)(m_fileStream.Length - 8));

                       //... and the dataChunksize header value;
                       m_fileStream.Seek(m_dataChunkSizePosition, SeekOrigin.Begin);
                       bw.Write((int)(m_fileStream.Length - (m_dataChunkSizePosition + 4)));
                    }
                }
                finally
                {
                    m_fileStream.Close();
                }
            }
        }
    }

    internal class WavFileSettings
    {
        public short CompressionCode { get; set; }
        public short NumberOfChannels { get; set; }
        public int SampleRate { get; set; }
        public int AvgBytesPerSecond { get; set; }

        /// <summary>
        /// Default constructor with default PCM 16 mono
        /// </summary>
        public WavFileSettings()
        {
            CompressionCode = 1;			//PCM
            NumberOfChannels = 1;			//No Stereo
            SampleRate = 16000;				//16khz only
            AvgBytesPerSecond = 32000;
        }
    }

from botbuilder-realtimemediacalling.

ssulzer avatar ssulzer commented on May 18, 2024

Are you trying to record within a multiparty group call? Recording the audio of individual participants in a group call is not supported. Please be aware that bots for group calls are currently not supported and such bots will likely stop working soon.

from botbuilder-realtimemediacalling.

gfclaveria avatar gfclaveria commented on May 18, 2024

@waboum Thanks! I will try this out.

@ssulzer Yes, I am hoping i could record multiparty group call. I currently working with the MeetingScreenshotsBot and it seems that bot is working as it should with group call. From what you've said, does that mean that this sample bot you have (or the feature it has) will be ditched out soon? May I know if this will be supported in the future. Also, bots for Skype For Business??

Thanks,

from botbuilder-realtimemediacalling.

ssulzer avatar ssulzer commented on May 18, 2024

@gfclaveria Upcoming changes will prevent calling and real-time media bots from joining Skype group calls, and unfortunately no timeframe regarding when they will be supported. Calling and real-time media bots will be allowed only in 1-to-1 Skype calls.

No information yet regarding calling and media bots for Skype for Business Online are not supported.

from botbuilder-realtimemediacalling.

gfclaveria avatar gfclaveria commented on May 18, 2024

@ssulzer Additional question. May we known the reason why will you be removing group calls feature for bot?

from botbuilder-realtimemediacalling.

MalarGit avatar MalarGit commented on May 18, 2024

@gfclaveria currently the bot can record audio and video of any user in the conference which is a privacy concern.

from botbuilder-realtimemediacalling.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.