Как воспроизвести аудио поток AAC с помощью NAudio? - PullRequest
0 голосов
/ 09 октября 2018

В приложении, над которым я сейчас работаю, я хотел бы воспроизвести аудиопоток AAC.Например, это интернет-радио: http://stream.bauermedia.fi/iskelma/kem_64.aac. Я хотел бы использовать платформу NAudio, так как она уже используется в этом проекте.

Для начала я рассмотрел Mp3StreamingDemo в NAudioDemo.Я создал класс ADTSFrame, где я обработал заголовок кадра ADTS (https://wiki.multimedia.cx/index.php/ADTS).. Из заголовка кадра ADTS я получил следующую информацию: частоту дискретизации, количество каналов, длину кадра. Затем я создал отдельный класс ADTSWaveFormat с одним частным полем waveFormatTagи два свойства Channels и SampleRate. WafeFormatTag установлен в WaveFormatEncoding.MPEG_ADTS_AAC. Как я понял отсюда - https://docs.microsoft.com/en-us/windows/desktop/DirectShow/audio-subtypes, больше никакой информации не требуется. Однако, когда я хотел получить формат PCM, AcmInterop.acmFormatSuggest () вернулся.«NoDriver». Поэтому я застрял здесь, так как не понимаю, двигаюсь ли я в правильном направлении.

Затем я искал в Интернете и нашел информацию, что в NAudio есть класс StreamMediaFoundationReader. StreamMediaFoundationReaderхочет получить поток в своем конструкторе. Прежде всего этот поток преобразуется в ComStream, а затем в IMFByteStream. Когда ComStream преобразуется в IMFByteStream, выполняется операция Seek. Поэтому ConnectStreamЯ получаю из response.GetResponseStream () не подходит для передачи конструктору StreamMediaFoundationReader, так как он не доступен для поиска.Поэтому я решил использовать поток памяти.

До сих пор наиболее успешный код, который я написал, был:

WebResponse response = WebRequest.Create(InputPath).GetResponse();
using (Stream rs = response.GetResponseStream())
{
  using (MemoryStream ms = new MemoryStream())
  {
    byte[] buffer = new byte[16384 * 4];
    int read = rs.Read(buffer, 0, buffer.Length);
    ms.Write(buffer, 0, read);
    using (var streamMfReader = new StreamMediaFoundationReader(rs))
    using (WaveOut waveOut = new WaveOut())
    {
      waveOut.Init(streamMfReader);
      waveOut.Play();
      while (waveOut.PlaybackState == PlaybackState.Playing)
        Thread.Sleep(100);
    }
  }
}

Как видите, этот код никуда не годится, так как он играет только длямомент.

Я также попробовал это:

MemoryStream memoryStream = new MemoryStream();
new Thread(delegate (object o)
{
  WebResponse response = WebRequest.Create(InputPath).GetResponse();
  using (Stream responseStream = response.GetResponseStream())
  {
    byte[] buffer = new byte[65536];
    int read;
    while ((read = responseStream.Read(buffer, 0, buffer.Length)) > 0)
    {
      long pos = memoryStream.Position;
      memoryStream.Position = memoryStream.Length;
      memoryStream.Write(buffer, 0, read);
      memoryStream.Position = pos;
    }
  }
}).Start();

while (memoryStream.Length < 65536 * 10)
  Thread.Sleep(1000);

memoryStream.Position = 0;
using (WaveStream streamMediaFoundationReader = new 
                                StreamMediaFoundationReader(memoryStream))
{
  using (WaveOut waveOut = new 
                             WaveOut(WaveCallbackInfo.FunctionCallback()))
  {
    waveOut.Init(streamMediaFoundationReader);
    waveOut.Play();
    while (waveOut.PlaybackState == PlaybackState.Playing)
    {
       Thread.Sleep(100);
    }
  }
}

Но здесь я получаю ContextSwitchDeadlock, так как memoryStream используется в разных потоках.

В настоящее время у меня не так много работыопыт программирования.

Не могли бы вы дать мне совет, как можно воспроизводить аудиопоток с помощью NAudio?

Сегодня я также попытался отредактировать ReadFullyStream следующим образом:

public class ReadFullyStream : Stream
    {
        private readonly Stream sourceStream;
        private long readPos;
        private long seekPos;
        private readonly byte[] readAheadBuffer;
        private int readAheadLength;
        private int readAheadOffset;

        public ReadFullyStream(Stream sourceStream)
        {
            this.sourceStream = sourceStream;
            readAheadBuffer = new byte[4096];
        }
        public override bool CanRead
        {
            get { return true; }
        }

        public override bool CanSeek
        {
            get { return true; }
        }

        public override bool CanWrite
        {
            get { return true; }
        }

        public override void Flush()
        {
            throw new InvalidOperationException();
        }

        public override long Length
        {
            get { return readPos; }
        }

        public override long Position
        {
            get => seekPos;
            set => seekPos = value;
        }


        public override int Read(byte[] buffer, int offset, int count)
        {
            int bytesRead = 0;
            while (bytesRead < count)
            {
                int readAheadAvailableBytes = readAheadLength - readAheadOffset;
                int bytesRequired = count - bytesRead;
                if (readAheadAvailableBytes > 0)
                {
                    int toCopy = Math.Min(readAheadAvailableBytes, bytesRequired);
                    Array.Copy(readAheadBuffer, readAheadOffset, buffer, offset + bytesRead, toCopy);
                    bytesRead += toCopy;
                    readAheadOffset += toCopy;
                }
                else
                {
                    readAheadOffset = 0;
                    readAheadLength = sourceStream.Read(readAheadBuffer, 0, readAheadBuffer.Length);
                    //Debug.WriteLine(String.Format("Read {0} bytes (requested {1})", readAheadLength, readAheadBuffer.Length));
                    if (readAheadLength == 0)
                    {
                        break;
                    }
                }
            }
            readPos += bytesRead;
            Position = readPos;
            return bytesRead;
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            Position = offset;
            return Position;
        }

        public override void SetLength(long value)
        {
            throw new InvalidOperationException();
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new InvalidOperationException();
        }
    }

Мое намерение состояло в том, чтобы сделать ReadFullyStream доступным для поиска, но на самом деле я понимаю, что не понимаю чего-то важного.После внесения этих изменений в классе ReadFullyStream я попробовал следующее:

WebRequest request = WebRequest.Create(InputPath);
WebResponse response = request.GetResponse();

using (Stream responseStream = response.GetResponseStream())
{
  ReadFullyStream readFullyStream = new ReadFullyStream(responseStream);
  using (WaveStream streamMediaFoundationReader = new 
                                 StreamMediaFoundationReader(readFullyStream))
  {
    using (WaveOut waveOut = new WaveOut())
    {
      waveOut.Init(streamMediaFoundationReader);
      waveOut.Play();
      while (waveOut.PlaybackState == PlaybackState.Playing)
      {
        Thread.Sleep(100);
      }
     }
   }
}

Теперь я получаю следующую ошибку в MediaFoundationApi CreateSourceReaderFromByteStream (IMFByteStream byteStream):

System.Runtime.InteropServices.COMException: «Катастрофический сбой (исключение из HRESULT: 0x8000FFFF (E_UNEXPECTED))».

...