qq_40557951 2022-03-07 17:42 采纳率: 50%
浏览 132
已结题

关于C# WaveOut 音频流播放问题

问题遇到的现象和发生背景

WaveOut播放音频流在一直开启音频接收线程时为什么持续几天后程序会突然挂掉?

问题相关代码,请勿粘贴截图
     public void StartUDP()
    {
        //开启UDP监听.....
        m_pUdpServer = new UdpServer();
   
        m_pUdpServer.Bindings = new IPEndPoint[] { new IPEndPoint(IPAddress.Parse(localIp), localUdpPort) };
        m_pUdpServer.PacketReceived += new PacketReceivedHandler(m_pUdpServer_PacketReceived);
        m_pUdpServer.Start();
        

    }
    

public void m_pUdpServer_PacketReceived(UdpPacket_eArgs e)
{
// Decompress data.
byte[] decodedData = null;//new byte[1024];
decodedData = G711.Decode_aLaw(e.Data, 0, e.Data.Length);
// recvNtwStream.Read(decodedData, 0, decodedData.Length);
string packStr = Helper.GetDecStringFromArray(e.Data);

        if (!(packStr.Contains("D5 D5 D5") || packStr.Contains("55 55 55")))
        {
            // decodedData = G711.Decode_uLaw(e.Data, 0, e.Data.Length);
            // We just play received packet.
            if (m_pWaveOut != null)
            {
                m_pWaveOut.Play(decodedData, 0, decodedData.Length);
               
              
            }
        }
    }

//内部调用
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;

using LumiSoft.Media.Wave.Native;

namespace LumiSoft.Media.Wave
{
///


/// This class implements streaming wav data player.
///

public class WaveOut : IDisposable
{
#region class PlayItem

    /// <summary>
    /// This class holds queued wav play item.
    /// </summary>
    internal class PlayItem
    {
        private GCHandle m_HeaderHandle;
        private GCHandle m_DataHandle;
        private int      m_DataSize = 0;

        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="headerHandle">Header handle.</param>
        /// <param name="header">Wav header.</param>
        /// <param name="dataHandle">Wav header data handle.</param>
        /// <param name="dataSize">Data size in bytes.</param>
        public PlayItem(ref GCHandle headerHandle,ref GCHandle dataHandle,int dataSize)
        {
            m_HeaderHandle = headerHandle;
            m_DataHandle   = dataHandle;
            m_DataSize     = dataSize;
        }

        #region method Dispose

        /// <summary>
        /// Cleans up any resources being used.
        /// </summary>
        public void Dispose()
        {
            m_HeaderHandle.Free();
            m_DataHandle.Free();
        }

        #endregion


        #region Properties Implementation

        /// <summary>
        /// Gets header handle.
        /// </summary>
        public GCHandle HeaderHandle
        {
            get{ return m_HeaderHandle; }
        }

        /// <summary>
        /// Gets header.
        /// </summary>
        public WAVEHDR Header
        {
            get{ return (WAVEHDR)m_HeaderHandle.Target; }
        }

        /// <summary>
        /// Gets wav header data pointer handle.
        /// </summary>
        public GCHandle DataHandle
        {
            get{ return m_DataHandle; }
        }

        /// <summary>
        /// Gets wav header data size in bytes.
        /// </summary>
        public int DataSize
        {
            get{ return m_DataSize; }
        }

        #endregion

    }

    #endregion

    private WavOutDevice    m_pOutDevice    = null;
    private int             m_SamplesPerSec = 8000;
    private int             m_BitsPerSample = 16;
    private int             m_Channels      = 1;
    private int             m_MinBuffer     = 1200;
    private IntPtr          m_pWavDevHandle = IntPtr.Zero;
    private int             m_BlockSize     = 0;
    private int             m_BytesBuffered = 0;
    private bool            m_IsPaused      = false;
    private List<PlayItem>  m_pPlayItems    = null;
    private waveOutProc     m_pWaveOutProc  = null;
    private bool            m_IsDisposed    = false;
    
    /// <summary>
    /// Default constructor.
    /// </summary>
    /// <param name="outputDevice">Output device.</param>
    /// <param name="samplesPerSec">Sample rate, in samples per second (hertz). For PCM common values are 
    /// 8.0 kHz, 11.025 kHz, 22.05 kHz, and 44.1 kHz.</param>
    /// <param name="bitsPerSample">Bits per sample. For PCM 8 or 16 are the only valid values.</param>
    /// <param name="channels">Number of channels.</param>
    /// <exception cref="ArgumentNullException">Is raised when <b>outputDevice</b> is null.</exception>
    /// <exception cref="ArgumentException">Is raised when any of the aruments has invalid value.</exception>
    public WaveOut(WavOutDevice outputDevice,int samplesPerSec,int bitsPerSample,int channels)
    {
        if(outputDevice == null){
            throw new ArgumentNullException("outputDevice");
        }
        if(samplesPerSec < 8000){
            throw new ArgumentException("Argument 'samplesPerSec' value must be >= 8000.");
        }
        if(bitsPerSample < 8){
            throw new ArgumentException("Argument 'bitsPerSample' value must be >= 8.");
        }
        if(channels < 1){
            throw new ArgumentException("Argument 'channels' value must be >= 1.");
        }

        m_pOutDevice    = outputDevice;
        m_SamplesPerSec = samplesPerSec;
        m_BitsPerSample = bitsPerSample;
        m_Channels      = channels;
        m_BlockSize     = m_Channels * (m_BitsPerSample / 8);
        m_pPlayItems    = new List<PlayItem>();
        
        // Try to open wav device.            
        WAVEFORMATEX format = new WAVEFORMATEX();
        format.wFormatTag      = WavFormat.PCM;
        format.nChannels       = (ushort)m_Channels;
        format.nSamplesPerSec  = (uint)samplesPerSec;                        
        format.nAvgBytesPerSec = (uint)(m_SamplesPerSec * m_Channels * (m_BitsPerSample / 8));
        format.nBlockAlign     = (ushort)m_BlockSize;
        format.wBitsPerSample  = (ushort)m_BitsPerSample;
        format.cbSize          = 0; 
        // We must delegate reference, otherwise GC will collect it.
        m_pWaveOutProc = new waveOutProc(this.OnWaveOutProc);
        int result = WavMethods.waveOutOpen(out m_pWavDevHandle,m_pOutDevice.Index,format,m_pWaveOutProc,0,WavConstants.CALLBACK_FUNCTION);
        if(result != MMSYSERR.NOERROR){
            throw new Exception("Failed to open wav device, error: " + result.ToString() + ".");
        }
    }

    /// <summary>
    /// Default destructor.
    /// </summary>
    ~WaveOut()
    {
        Dispose();
    }

    #region method Dispose

    /// <summary>
    /// Cleans up any resources being used.
    /// </summary>
    public void Dispose()
    {
        if(m_IsDisposed){
            return;
        }
        m_IsDisposed = true;

        try{
            // If playing, we need to reset wav device first.
            WavMethods.waveOutReset(m_pWavDevHandle);

            // If there are unprepared wav headers, we need to unprepare these.
            foreach(PlayItem item in m_pPlayItems){
                WavMethods.waveOutUnprepareHeader(m_pWavDevHandle,item.HeaderHandle.AddrOfPinnedObject(),Marshal.SizeOf(item.Header));
                item.Dispose();
            }
            
            // Close output device.
            WavMethods.waveOutClose(m_pWavDevHandle);

            m_pOutDevice    = null;
            m_pWavDevHandle = IntPtr.Zero;
            m_pPlayItems    = null;
            m_pWaveOutProc  = null;
        }
        catch{                
        }
    }

    #endregion


    #region method OnWaveOutProc

    /// <summary>
    /// This method is called when wav device generates some event.
    /// </summary>
    /// <param name="hdrvr">Handle to the waveform-audio device associated with the callback.</param>
    /// <param name="uMsg">Waveform-audio output message.</param>
    /// <param name="dwUser">User-instance data specified with waveOutOpen.</param>
    /// <param name="dwParam1">Message parameter.</param>
    /// <param name="dwParam2">Message parameter.</param>
    private void OnWaveOutProc(IntPtr hdrvr,int uMsg,int dwUser,int dwParam1,int dwParam2)
    {   
        // NOTE: MSDN warns, we may not call any wav related methods here.

        try{
            if(uMsg == WavConstants.MM_WOM_DONE){ 
                ThreadPool.QueueUserWorkItem(new WaitCallback(this.OnCleanUpFirstBlock));
            }
        }
        catch{
        }
    }

    #endregion

    #region method OnCleanUpFirstBlock

    /// <summary>
    /// Cleans up the first data block in play queue.
    /// </summary>
    /// <param name="state">User data.</param>
    private void OnCleanUpFirstBlock(object state)
    {
        try{            
            lock(m_pPlayItems){
                PlayItem item = m_pPlayItems[0];
                WavMethods.waveOutUnprepareHeader(m_pWavDevHandle,item.HeaderHandle.AddrOfPinnedObject(),Marshal.SizeOf(item.Header));                    
                m_pPlayItems.Remove(item);
                m_BytesBuffered -= item.DataSize;
                item.Dispose();
            }
        }
        catch{
        }
    }

    #endregion


    #region method Play

    /// <summary>
    /// Plays specified audio data bytes. If player is currently playing, data will be queued for playing.
    /// </summary>
    /// <param name="audioData">Audio data. Data boundary must n * BlockSize.</param>
    /// <param name="offset">Offset in the buffer.</param>
    /// <param name="count">Number of bytes to play form the specified offset.</param>
    /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
    /// <exception cref="ArgumentNullException">Is raised when <b>audioData</b> is null.</exception>
    /// <exception cref="ArgumentException">Is raised when <b>audioData</b> is with invalid length.</exception>
    public void Play(byte[] audioData,int offset,int count)
    {
        if(m_IsDisposed){
            throw new ObjectDisposedException("WaveOut");
        }
        if(audioData == null){
            throw new ArgumentNullException("audioData");
        }
        if((count % m_BlockSize) != 0){
            throw new ArgumentException("Audio data is not n * BlockSize.");
        }

        //--- Queue specified audio block for play. --------------------------------------------------------
        byte[]   data       = new byte[count];
        Array.Copy(audioData,offset,data,0,count);
        GCHandle dataHandle = GCHandle.Alloc(data,GCHandleType.Pinned);

// m_BytesBuffered += data.Length;

        WAVEHDR wavHeader = new WAVEHDR();
        wavHeader.lpData          = dataHandle.AddrOfPinnedObject();
        wavHeader.dwBufferLength  = (uint)data.Length;
        wavHeader.dwBytesRecorded = 0;
        wavHeader.dwUser          = IntPtr.Zero;
        wavHeader.dwFlags         = 0;
        wavHeader.dwLoops         = 0;
        wavHeader.lpNext          = IntPtr.Zero;
        wavHeader.reserved        = 0;
        GCHandle headerHandle = GCHandle.Alloc(wavHeader,GCHandleType.Pinned);
        int result = 0;        
        result = WavMethods.waveOutPrepareHeader(m_pWavDevHandle,headerHandle.AddrOfPinnedObject(),Marshal.SizeOf(wavHeader));
        if(result == MMSYSERR.NOERROR){
            PlayItem item = new PlayItem(ref headerHandle,ref dataHandle,data.Length);
            m_pPlayItems.Add(item);

            // We ran out of minimum buffer, we must pause playing while min buffer filled.
            if(m_BytesBuffered < 1000){
                if(!m_IsPaused){
                    WavMethods.waveOutPause(m_pWavDevHandle);
                    m_IsPaused = true;
                }
                //File.AppendAllText("aaaa.txt","Begin buffer\r\n");
            }
            // Buffering completed,we may resume playing.
            else if(m_IsPaused && m_BytesBuffered > m_MinBuffer){
                WavMethods.waveOutRestart(m_pWavDevHandle);
                m_IsPaused = false;
                //File.AppendAllText("aaaa.txt","end buffer: " + m_BytesBuffered + "\r\n");
            }
            /*
            // TODO: If we ran out of minimum buffer, we must pause playing while min buffer filled.
            if(m_BytesBuffered < m_MinBuffer){
                if(!m_IsPaused){
                    WavMethods.waveOutPause(m_pWavDevHandle);
                    m_IsPaused = true;
                }
            }
            else if(m_IsPaused){
                WavMethods.waveOutRestart(m_pWavDevHandle);
            }*/

            m_BytesBuffered += data.Length;

            result = WavMethods.waveOutWrite(m_pWavDevHandle,headerHandle.AddrOfPinnedObject(),Marshal.SizeOf(wavHeader));
        }
        else{
            dataHandle.Free();
            headerHandle.Free();
        }
        //--------------------------------------------------------------------------------------------------
    }

    #endregion

    #region method GetVolume

    /// <summary>
    /// Gets audio output volume.
    /// </summary>
    /// <param name="left">Left channel volume level.</param>
    /// <param name="right">Right channel volume level.</param>
    /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
    public void GetVolume(ref ushort left,ref ushort right)
    {
        if(m_IsDisposed){
            throw new ObjectDisposedException("WaveOut");
        }

        int volume = 0;
        WavMethods.waveOutGetVolume(m_pWavDevHandle,out volume);

        left  = (ushort)(volume & 0x0000ffff);
        right = (ushort)(volume >> 16);
    }

    #endregion

    #region method SetVolume

    /// <summary>
    /// Sets audio output volume.
    /// </summary>
    /// <param name="left">Left channel volume level.</param>
    /// <param name="right">Right channel volume level.</param>
    /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
    public void SetVolume(ushort left,ushort right)
    {
        if(m_IsDisposed){
            throw new ObjectDisposedException("WaveOut");
        }

        WavMethods.waveOutSetVolume(m_pWavDevHandle,(right << 16 | left & 0xFFFF));
    }

    #endregion


    #region Properties Implementation

    /// <summary>
    /// Gets all available output audio devices.
    /// </summary>
    public static WavOutDevice[] Devices
    {
        get{
            List<WavOutDevice> retVal = new List<WavOutDevice>();
            // Get all available output devices and their info.
            int devicesCount = WavMethods.waveOutGetNumDevs();
            for(int i=0;i<devicesCount;i++){
                WAVEOUTCAPS pwoc = new WAVEOUTCAPS();
                if(WavMethods.waveOutGetDevCaps((uint)i,ref pwoc,Marshal.SizeOf(pwoc)) == MMSYSERR.NOERROR){
                    retVal.Add(new WavOutDevice(i,pwoc.szPname,pwoc.wChannels));
                }
            }

            return retVal.ToArray(); 
        }
    }


    /// <summary>
    /// Gets if this object is disposed.
    /// </summary>
    public bool IsDisposed
    {
        get{ return m_IsDisposed; }
    }

    /// <summary>
    /// Gets current output device.
    /// </summary>
    /// <exception cref="">Is raised when this object is disposed and this property is accessed.</exception>
    public WavOutDevice OutputDevice
    {
        get{
            if(m_IsDisposed){
                throw new ObjectDisposedException("WaveOut");
            }

            return m_pOutDevice; 
        }
    }

    /// <summary>
    /// Gets number of samples per second.
    /// </summary>
    /// <exception cref="">Is raised when this object is disposed and this property is accessed.</exception>
    public int SamplesPerSec
    {
        get{                 
            if(m_IsDisposed){
                throw new ObjectDisposedException("WaveOut");
            }

            return m_SamplesPerSec; 
        }
    }

    /// <summary>
    /// Gets number of buts per sample.
    /// </summary>
    /// <exception cref="">Is raised when this object is disposed and this property is accessed.</exception>
    public int BitsPerSample
    {
        get{ 
            if(m_IsDisposed){
                throw new ObjectDisposedException("WaveOut");
            }
            
            return m_BitsPerSample; 
        }
    }

    /// <summary>
    /// Gets number of channels.
    /// </summary>
    /// <exception cref="">Is raised when this object is disposed and this property is accessed.</exception>
    public int Channels
    {
        get{ 
            if(m_IsDisposed){
                throw new ObjectDisposedException("WaveOut");
            }
            
            return m_Channels; 
        }
    }

    /// <summary>
    /// Gets one smaple block size in bytes.
    /// </summary>
    /// <exception cref="">Is raised when this object is disposed and this property is accessed.</exception>
    public int BlockSize
    {
        get{ 
            if(m_IsDisposed){
                throw new ObjectDisposedException("WaveOut");
            }

            return m_BlockSize; 
        }
    }

    /// <summary>
    /// Gets if wav player is currently playing something.
    /// </summary>
    /// <exception cref="">Is raised when this object is disposed and this property is accessed.</exception>
    public bool IsPlaying
    {
        get{
            if(m_IsDisposed){
                throw new ObjectDisposedException("WaveOut");
            }
            
            if(m_pPlayItems.Count > 0){
                return true;
            }
            else{
                return false;
            }
        }
    }

    #endregion

}

}

请问有什么好的方法可以解决这个问题吗??

  • 写回答

1条回答 默认 最新

  • qq_40557951 2022-03-08 11:39
    关注

    有没有大佬可以帮忙提供一下建议啊??

    评论

报告相同问题?

问题事件

  • 系统已结题 3月15日
  • 创建了问题 3月7日

悬赏问题

  • ¥50 导入文件到网吧的电脑并且在重启之后不会被恢复
  • ¥15 (希望可以解决问题)ma和mb文件无法正常打开,打开后是空白,但是有正常内存占用,但可以在打开Maya应用程序后打开场景ma和mb格式。
  • ¥20 ML307A在使用AT命令连接EMQX平台的MQTT时被拒绝
  • ¥20 腾讯企业邮箱邮件可以恢复么
  • ¥15 有人知道怎么将自己的迁移策略布到edgecloudsim上使用吗?
  • ¥15 错误 LNK2001 无法解析的外部符号
  • ¥50 安装pyaudiokits失败
  • ¥15 计组这些题应该咋做呀
  • ¥60 更换迈创SOL6M4AE卡的时候,驱动要重新装才能使用,怎么解决?
  • ¥15 让node服务器有自动加载文件的功能