2016-09-26 15 views
7

Tôi có một lớp học để giúp tôi phát các tệp mp3 từ các nguồn URL. Nó hoạt động tốt khi chơi, tạm dừng và tiếp tục. Nhưng tôi đang bối rối về việc tiến nhanh hoặc lùi.Làm thế nào để tua nhanh hoặc lùi một tập tin mp3 trong khi phát?

Tôi đang sử dụng tệp tạm thời để lưu trữ dữ liệu mp3 và tôi muốn định vị lại FileStream theo vị trí mà người dùng đã chọn. Nhưng có một vấn đề cho nó.

Sự cố: Nếu vị trí chưa tồn tại. (Không tải chưa) enter image description here

Đây có thể được giải quyết bằng WebRequest.AddRange() nhưng trong trường hợp này, chúng ta phải mở một FileStream mới để chứa byte riêng biệt và kêu gọi AddRange() phương pháp mỗi lần người dùng mà muốn đi về phía trước hoặc phía sau có nghĩa là các tập tin sẽ được tải xuống lại từ vị trí đó. Tuy nhiên, nếu điều này được thực hiện quá thường xuyên, chúng tôi phải tải xuống tệp nhiều như số lượng tiến hoặc lùi.

Vì vậy, nếu có giải pháp đơn giản và thân thiện với hạn ngạch, vui lòng cho tôi biết. Tôi không thể tìm ra cách để làm điều đó. Xin vui lòng giúp đỡ!

đang My:

public class NAudioPlayer 
{ 
    HttpWebRequest req; 
    HttpWebResponse resp; 
    Stream stream; 
    WaveOut waveOut; 
    Mp3WaveFormat format; 
    AcmMp3FrameDecompressor decompressor; 
    BufferedWaveProvider provider; 
    FileStream tempFileStream; 
    System.Windows.Forms.Timer ticker; 
    private int bufferedDuration; 

    string url, path; 
    long size, streamPos; 
    int timeOffset, timePosition, avgBytes, duration; 
    bool formatKnown, waitinloop, exitloop; 

    State currentState; 

    public NAudioPlayer(string mp3Url) 
    { 
     this.url = mp3Url; 
     this.currentState = State.Stopped; 
     this.size = -1; 
     this.timeOffset = 0; 
     this.timePosition = 0; 
     this.avgBytes = 0; 
     this.duration = 0; 
     this.format = null; 
     this.ticker = new System.Windows.Forms.Timer(); 
     this.waveOut = new WaveOut(); 
     this.waitinloop = false; 

     ticker.Interval = 250; 
     ticker.Tick += ticker_Tick; 

    } 
    int target = 0; 
    void ticker_Tick(object sender, EventArgs e) 
    { 
     if (waveOut.PlaybackState == PlaybackState.Playing) 
     { 
      timePosition = timeOffset + (int)(waveOut.GetPosition() * 1d/waveOut.OutputWaveFormat.AverageBytesPerSecond); 
      Debug.WriteLine(timePosition); 
     } 
     if (duration != 0 && timePosition >= duration) 
     { 
      waveOut.Stop(); 
      ticker.Stop(); 
     } 

     if (timePosition == target && timePosition < duration - 5 && 
      provider != null && provider.BufferedDuration.TotalSeconds < 5) 
     { 
      waveOut.Pause(); 
      currentState = State.Buffering; 
      target = timePosition + 5; 
     } 
     if (currentState == State.Buffering && provider != null && provider.BufferedDuration.TotalSeconds >= 5) 
     { 
      waveOut.Play(); 
     } 
    } 

    public void Play() 
    { 
     int range = avgBytes <= 0 ? 0 : timeOffset * avgBytes; 
     int readBytes = 0; 
     long pos = 0; 
     this.streamPos = 0; 
     exitloop = false; 
     disposeAllResources(); 
     ticker.Start(); 

     Task.Run(() => 
     { 

      //Crate WebRequest using AddRange to enable repositioning the mp3 
      req = WebRequest.Create(url) as HttpWebRequest; 
      req.AllowAutoRedirect = true; 
      req.ServicePoint.ConnectionLimit = 100; 
      req.UserAgent = "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:31.0) Gecko/20100101 Firefox/31.0"; 
      req.AddRange(range); 
      resp = req.GetResponse() as HttpWebResponse; 
      stream = resp.GetResponseStream(); 
      size = resp.ContentLength; 

      //Create a unique file to store data 
      path = Path.GetTempPath() + Guid.NewGuid().ToString() + ".mp3"; 
      tempFileStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite); 

      waveOut.Stop(); 
      waveOut = new WaveOut(); 
      if (provider != null) 
       waveOut.Init(provider); 

      byte[] buffer = new byte[17 * 1024]; 

      while ((readBytes = stream.Read(buffer, 0, buffer.Length)) > 0 || 
        timePosition <= duration) 
      { 
       while (waitinloop) 
        Thread.Sleep(500); 

       if (exitloop) 
        break; 

       Mp3Frame frame = null; 
       tempFileStream.Write(buffer, 0, readBytes); 
       tempFileStream.Flush(); 

       //Read the stream starting from the point 
       //where we were at the last reading 
       using (MemoryStream ms = new MemoryStream(ReadStreamPartially(tempFileStream, streamPos, 1024 * 10))) 
       { 
        ms.Position = 0; 
        try 
        { 
         frame = Mp3Frame.LoadFromStream(ms); 
        } 
        catch { continue; } //Sometimes it throws Unexpected End of Stream exception 
        //Couldn't find the problem out, try catch is working for now 

        if (frame == null) 
         continue; 

        pos = ms.Position; 
        streamPos += pos; 
       } 

       if (!formatKnown) 
       { 
        format = new Mp3WaveFormat(frame.SampleRate, frame.ChannelMode == ChannelMode.Mono ? 1 : 2, 
                   frame.FrameLength, frame.BitRate); 
        duration = (int)(Math.Ceiling(resp.ContentLength * 1d/format.AverageBytesPerSecond)); 

        avgBytes = format.AverageBytesPerSecond; 
        formatKnown = true; 
       } 

       if (decompressor == null) 
       { 
        decompressor = new AcmMp3FrameDecompressor(format); 
        provider = new BufferedWaveProvider(decompressor.OutputFormat); 
        provider.BufferDuration = TimeSpan.FromSeconds(20); 
        waveOut.Init(provider); 
        waveOut.Play(); 
       } 

       int decompressed = decompressor.DecompressFrame(frame, buffer, 0); 

       if (IsBufferNearlyFull(provider)) 
       { 
        Thread.Sleep(500); 
       } 


       provider.AddSamples(buffer, 0, decompressed); 
      } 
     }); 
    } 


    void disposeAllResources() 
    { 
     if (resp != null) 
      resp.Close(); 
     if (stream != null) 
      stream.Close(); 
     if (provider != null) 
      provider.ClearBuffer(); 
    } 

    public void Pause() 
    { 
     if (waveOut.PlaybackState == PlaybackState.Playing && !waitinloop) 
     { 
      waitinloop = true; 
      waveOut.Pause(); 
      Thread.Sleep(200); 
     } 
    } 
    public void Resume() 
    { 
     if (waveOut.PlaybackState == PlaybackState.Paused && waitinloop) 
     { 
      waitinloop = false; 
      waveOut.Play(); 
      Thread.Sleep(200); 
     } 
    } 
    public void ForwardOrBackward(int targetTimePos) 
    { 
     waitinloop = false; 
     exitloop = true; 
     timeOffset = targetTimePos; 
     Thread.Sleep(100); 
     waveOut.Stop(); 
     ticker.Stop(); 
     this.Play(); 
    } 
    public static byte[] ReadStreamPartially(System.IO.Stream stream, long offset, long count) 
    { 
     long originalPosition = 0; 

     if (stream.CanSeek) 
     { 
      originalPosition = stream.Position; 
      stream.Position = offset; 
     } 

     try 
     { 
      byte[] readBuffer = new byte[4096]; 
      byte[] total = new byte[count]; 
      int totalBytesRead = 0; 
      int byteRead; 

      while ((byteRead = stream.ReadByte()) != -1) 
      { 
       Buffer.SetByte(total, totalBytesRead, (byte)byteRead); 
       totalBytesRead++; 
       if (totalBytesRead == count) 
       { 
        stream.Position = originalPosition; 
        break; 
       } 
      } 
      if (totalBytesRead < count) 
      { 
       byte[] temp = new byte[totalBytesRead]; 
       Buffer.BlockCopy(total, 0, temp, 0, totalBytesRead); 
       stream.Position = originalPosition; 
       return temp; 
      } 
      return total; 
     } 
     finally 
     { 
      if (stream.CanSeek) 
      { 
       stream.Position = originalPosition; 
      } 
     } 
    } 
    private bool IsBufferNearlyFull(BufferedWaveProvider bufferedWaveProvider) 
    { 
     return bufferedWaveProvider != null && 
       bufferedWaveProvider.BufferLength - bufferedWaveProvider.BufferedBytes 
       < bufferedWaveProvider.WaveFormat.AverageBytesPerSecond/4; 
    } 

    public int Duration 
    { 
     get 
     { 
      return duration; 
     } 
    } 
    public int TimePosition 
    { 
     get 
     { 
      return timePosition; 
     } 
    } 
    public int BufferedDuration 
    { 
     get { return (int)provider.BufferedDuration.TotalSeconds; } 
    } 
    public int TimeOffset 
    { 
     get 
     { 
      return timeOffset; 
     } 
    } 
} 
public enum State 
{ 
    Paused, 
    Playing, 
    Stopped, 
    Buffering 
} 
+0

Bạn có thể cung cấp mã đầy đủ (bao gồm giao diện người dùng), có lẽ là giải pháp visual studio độc lập? – Evk

+0

Thành thật mà nói, tôi hiện đang thiết kế giao diện người dùng để tạo thanh trình phát để hiển thị thời gian hiện tại và cho phép người dùng tua nhanh hoặc tua lại. Nhưng liệu nó có thực sự cần thiết để suy nghĩ về giải pháp này? –

Trả lời

5

tôi thể chỉ cho bạn, làm thế nào tôi sẽ cố gắng để làm điều đó - giả sử rằng bộ đệm của "waveOut" không phải là hoàn toàn khác nhau để một DirectSound SecondaryBuffer.

Chơi một Stream có thể cư xử như thế này:

The way it's meant to be played.

Có dữ liệu allready tải về và có thể chơi và không được tải về dữ liệu ở giữa. Để lưu dữ liệu được tải xuống được phân đoạn này, chúng tôi cần thêm thông tin bổ sung vào nó - thời gian \ playorder.

Để dễ dàng hơn, chúng tôi chia tệp/luồng trong các phần con nguyên tử có kích thước cố định, ví dụ: 100kByte. Nếu tập tin là 5001 kByte -> 51 Subchunks là cần thiết.

Bạn có thể lưu chúng vào một tệp theo thứ tự đã tải xuống và tìm id int bạn cần - sau đó tải lại subchunk trong bộ phát của bạn. Để làm như vậy bạn phải sử dụng phiên bản này của AddRange để tải một subchunk:

public void AddRange (int từ, int để) https://msdn.microsoft.com/de-de/library/7fy67z6d(v=vs.110).aspx

enter image description here

tôi hy vọng bạn sẽ có được điểm.

  • tải với phương pháp khác và giữ cũ dòng

  • Hãy thử playbuffer nếu bơm queque mình là cần thiết.

  • Chỉ tải xuống một phần phụ nếu nó chưa được lưu trong bộ nhớ hoặc tệp.

Một cách đọc vào một tập tin có thể được xử lý:

File description

+0

Giải pháp thú vị và tôi chưa bao giờ thấy nó trước đây. Trong cách giải quyết này, cách chúng tôi viết đoạn dữ liệu vào tệp nếu người dùng đã nhảy đến một vị trí? Ý tôi là nếu các khối được tải xuống là '[1] [2] [3] [4]' và người dùng tiến nhanh về phía đoạn 11. Vậy chúng ta có thể tạo ra các tập tin như '[1] [2] [3] [4] [11] [12]' ... mà không cần viết các đoạn giữa khối thứ 4 và 11? –

+1

Có, nó sẽ là có thể, bởi vì thứ tự "bên trong" được mô tả bởi id đoạn. Tôi đã cập nhật câu trả lời của mình bằng một ít thông tin. –

+0

Florian, Đây là một giải pháp tuyệt vời cho vấn đề của tôi. Tôi đang làm việc với phương pháp này bây giờ và nó đang làm việc. Cám ơn bạn một lần nữa. –

Các vấn đề liên quan