播放HLS播放列表

时间:2018-11-12 07:30:50

标签: c# naudio hls internet-radio naudio-framework

我正在尝试编写一个程序,它将播放HLS播放列表。例如,我有一个指向互联网广播的链接:https://digitacdn.akamaized.net/hls/live/629243/radiosuomipop/master-128000.m3u8。我正在使用NAudio。到目前为止,我已经编写了下一个代码:

using System;
using System.Collections.Concurrent;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using NAudio.Wave;
using NAudioWpfDemo.ViewModel;

namespace NAudioWpfDemo.MediaFoundationPlayback
{
    internal class MediaFoundationPlaybackViewModel : ViewModelBase, IDisposable
    {
        enum ShoutCastPlaybackState
        {
            Stopped,
            Playing,
            Buffering
        }

        private const string SegmentTargetDuration = "EXT-X-TARGETDURATION";
        private const string SegmentInfo = "#EXTINF";

        private bool isBuffering;
        private string inputPath;

        public RelayCommand PlayCommand { get; }
        public RelayCommand StopCommand { get; }


        private IWavePlayer waveOut;
        private BufferedWaveProvider bufferedWaveProvider;
        private volatile ShoutCastPlaybackState playbackState;
        private readonly ConcurrentQueue<string> foundLinks = new ConcurrentQueue<string>();
        private string previousLink;
        private int segmentDuration;
        private HttpWebRequest webRequest;

        public MediaFoundationPlaybackViewModel()
        {
            PlayCommand = new RelayCommand(() =>
                {
                    playbackState = ShoutCastPlaybackState.Buffering;
                    bufferedWaveProvider = null;
                    Thread webRequestThread = new Thread(GetSegmentLinks);
                    webRequestThread.Start();
                    Thread bufferFillerThread = new Thread(FillBuffer);
                    bufferFillerThread.Start();
                    Thread playerThread = new Thread(Play);
                    playerThread.Start();
                },
                () =>
                    IsStopped
            );
            StopCommand = new RelayCommand(Stop, () => !IsStopped);
        }

        private void Play()
        {
            while (!IsStopped)
            {
                if (waveOut != null || bufferedWaveProvider == null) continue;

                waveOut = new WaveOut();
                waveOut.Init(bufferedWaveProvider);
                waveOut.Play();
                playbackState = ShoutCastPlaybackState.Playing;
                OnPropertyChanged(nameof(IsStopped));
                while (waveOut!= null && waveOut.PlaybackState == PlaybackState.Playing)
                {
                    Thread.Sleep(1000);
                }
            }
        }

        public bool IsStopped => playbackState == ShoutCastPlaybackState.Stopped;

        public bool IsBuffering
        {
            get => isBuffering;
            set
            {
                if (isBuffering == value) return;
                isBuffering = value;
                OnPropertyChanged(nameof(IsBuffering));
                OnPropertyChanged(nameof(IsStopped));
            }
        }

        public string InputPath
        {
            get => inputPath;
            set
            {
                if (inputPath != value)
                {
                    inputPath = value;
                    OnPropertyChanged("InputPath");
                }
            }
        }

        private void Stop()
        {
            if (waveOut == null) return;
            waveOut.Stop();
            waveOut.Dispose();
            waveOut = null;
            playbackState = ShoutCastPlaybackState.Stopped;
        }

        private void FillBuffer()
        {
            Uri baseUri = new Uri(InputPath);
            while (!IsStopped)
            {
                if (IsBufferNearlyFull)
                {
                    Thread.Sleep(500);
                }
                else
                {
                    foundLinks.TryPeek(out string link);

                    if (link == null) continue;

                    foundLinks.TryDequeue(out link);
                    Uri resourceUri = new Uri(baseUri, link);

                    MediaFoundationReader mediaFoundationReader;
                    try
                    {
                        mediaFoundationReader =
                            new MediaFoundationReader(resourceUri.AbsoluteUri);
                    }
                    catch(Exception e)
                    {
                        continue;
                    }
                    byte[] buffer = new byte [mediaFoundationReader.WaveFormat.AverageBytesPerSecond * segmentDuration *2];
                    int readBytes = mediaFoundationReader.Read(buffer, 0,
                        mediaFoundationReader.WaveFormat.AverageBytesPerSecond * segmentDuration * 2);

                    if (bufferedWaveProvider == null)
                    {
                        bufferedWaveProvider = new BufferedWaveProvider(mediaFoundationReader.WaveFormat)
                        {
                            BufferDuration = TimeSpan.FromSeconds(segmentDuration * 5)
                        };
                    }

                    bufferedWaveProvider.AddSamples(buffer, 0, readBytes);
                    mediaFoundationReader.Dispose();
                }
            }
        }

        private void GetSegmentLinks()
        {
            while (!IsStopped)
                try
                {
                    webRequest = (HttpWebRequest) WebRequest.Create(InputPath);
                    HttpWebResponse response = (HttpWebResponse) webRequest.GetResponse();
                    Encoding encoding = Encoding.UTF8;
                    Stream responseStream = response.GetResponseStream();
                    if (responseStream == null) continue;

                    using (StreamReader streamReader = new StreamReader(responseStream, encoding))
                    {
                        string line;
                        while ((line = streamReader.ReadLine()) != null)
                        {
                            if (line.Contains(SegmentTargetDuration))
                            {
                                segmentDuration = Convert.ToInt32(line.Split(':')[1]);
                            }
                            else if (line.Contains(SegmentInfo))
                            {
                                line = streamReader.ReadLine();
                                if (line != null && string.Compare(line, previousLink, true, CultureInfo.InvariantCulture) > 0)
                                {
                                    foundLinks.Enqueue(line);
                                    previousLink = line;
                                }

                            }
                        }
                    }
                }
                catch (Exception)
                {
                    // ignored
                }
        }

        private bool IsBufferNearlyFull => bufferedWaveProvider != null &&
                                           bufferedWaveProvider.BufferLength - bufferedWaveProvider.BufferedBytes
                                           < bufferedWaveProvider.WaveFormat.AverageBytesPerSecond * segmentDuration;

        public void Dispose()
        {
            waveOut?.Dispose();
        }
    }

}

我知道,这段代码并不理想。

我执行HTTP请求并部分解析响应。响应是这样的:

#EXTM3U
#EXT-X-VERSION:3
## Created with Z/IPStream R/2 v1.03.23
#EXT-X-MEDIA-SEQUENCE:3230447
#EXT-X-TARGETDURATION:6
#EXT-X-PROGRAM-DATE-TIME:2018-11-09T12:57:20Z
#EXTINF:5.99, no desc
01616/seg128000-03230447.aac
#EXTINF:5.99, no desc
01616/seg128000-03230448.aac
#EXTINF:5.99, no desc
01616/seg128000-03230449.aac
#EXTINF:5.99, no desc
01616/seg128000-03230450.aac
#EXTINF:5.99, no desc
01616/seg128000-03230451.aac
#EXTINF:5.99, no desc
01616/seg128000-03230452.aac

到音频文件段的链接被添加到队列中。为了相同的段不会多次添加到队列中,我将新段的名称与最后添加到队列中的段的名称进行比较。如果新段的名称在前一个段之后,则将该段添加到队列中。 我想知道当指向段的链接为99999/seg128000-99999999.aac时会发生什么?下一个分段的链接是什么? 00001/seg128000-00000001.aac?如果是,则有时链接将不再添加到队列中,该程序将无法工作。那我应该怎么做才能使同一段不播放几次呢?一种机会是使用HashSet,但是有时应从其中删除链接,而我不知道何时。谁能写出解决这个问题的方法?

0 个答案:

没有答案