2016-01-11 21 views
16

Hiện tại tôi có luồng trực tiếp đang hoạt động bằng cách sử dụng webapi. Bằng cách nhận một luồng flv trực tiếp từ ffmpeg và gửi nó thẳng đến máy khách bằng PushStreamContent. Điều này hoạt động hoàn toàn tốt nếu trang web đã mở khi luồng bắt đầu. Vấn đề là khi tôi mở một trang khác hoặc làm mới trang này, bạn không còn có thể xem luồng nữa (luồng vẫn đang được gửi đến ứng dụng khách). Tôi nghĩ rằng đó là do một cái gì đó mất tích từ đầu dòng nhưng tôi không chắc chắn phải làm gì. Bât cư thông tin được cung câp nao cung được la sự suât hiện tuyệt vơi.Phát trực tuyến FLV trong C# WebApi

Mã cho khách hàng đọc dòng

public class VideosController : ApiController 
{ 
    public HttpResponseMessage Get() 
    { 
     var response = Request.CreateResponse(); 
     response.Content = new PushStreamContent(WriteToStream, new MediaTypeHeaderValue("video/x-flv")); 

     return response; 
    } 

    private async Task WriteToStream(Stream arg1, HttpContent arg2, TransportContext arg3) 
    { 
     //I think metadata needs to be written here but not sure how 
     Startup.AddSubscriber(arg1); 
     await Task.Yield(); 
    } 
} 

Mã cho nhận được luồng và sau đó gửi cho khách hàng

while (true) 
{ 
    bytes = new byte[8024000]; 
    int bytesRec = handler.Receive(bytes); 

    foreach (var subscriber in Startup.Subscribers.ToList()) 
    { 
     var theSubscriber = subscriber; 
     try 
     { 
      await theSubscriber.WriteAsync(bytes, 0, bytesRec); 
     } 
     catch 
     { 
      Startup.Subscribers.Remove(theSubscriber); 
     } 
    } 
} 

Trả lời

2

Tôi chưa bao giờ sử dụng FLV hoặc nghiên cứu các định dạng video chặt chẽ

Hầu hết các định dạng tệp đều được cấu trúc, đặc biệt là định dạng video. Chúng chứa các khung (tức là một ảnh chụp màn hình hoàn chỉnh hoặc một phần tùy thuộc vào định dạng nén).

Bạn sẽ thực sự may mắn nếu bạn quản lý để đạt được một khung hình cụ thể khi bạn bắt đầu phát trực tuyến tới người đăng ký mới. Do đó khi họ bắt đầu nhận được dòng họ không thể xác định được định dạng như khung là một phần.

Bạn có thể đọc thêm khung FLV trong wikipedia article. Điều này rất có thể là vấn đề của bạn.

Nỗ lực đơn giản là cố gắng lưu tiêu đề ban đầu mà bạn nhận được từ máy chủ phát trực tuyến khi người đăng ký đầu tiên kết nối.

Cái gì như:

static byte _header = new byte[9]; //signature, version, flags, headerSize 

public void YourStreamMethod() 
{ 
    int bytesRec = handler.Receive(bytes); 
    if (!_headerIsStored) 
    { 
     //store header 
     Buffer.BlockCopy(bytes, 0, _header, 0, 9); 
     _headerIsStored = true; 
    } 
} 

.. mà cho phép bạn gửi các tiêu đề để các thuê bao kết nối tiếp theo:

private async Task WriteToStream(Stream arg1, HttpContent arg2, TransportContext arg3) 
{ 
    // send the FLV header 
    arg1.Write(_header, 0, 9); 

    Startup.AddSubscriber(arg1); 
    await Task.Yield(); 
} 

Khi đã xong, cầu nguyện rằng người nhận sẽ bỏ qua khung phần. Nếu không, bạn cần phân tích luồng để xác định vị trí khung tiếp theo.

Để làm điều đó bạn cần phải làm điều gì đó như thế này:

  1. Tạo một biến BytesLeftToNextFrame.
  2. Store tiêu đề gói tin nhận được trong một bộ đệm
  3. Chuyển đổi "kích thước Payload" bit đến một int
  4. Thiết lập lại BytesLeftToNextFrame với giá trị phân tích cú pháp
  5. Đếm ngược cho đến khi thời gian tiếp theo bạn nên đọc một tiêu đề.

Cuối cùng, khi khách hàng mới kết nối, không bắt đầu phát trực tuyến cho đến khi bạn biết rằng khung tiếp theo đến.

Pseudo mã:

var bytesLeftToNextFrame = 0; 
while (true) 
{ 
    bytes = new byte[8024000]; 
    int bytesRec = handler.Receive(bytes); 

    foreach (var subscriber in Startup.Subscribers.ToList()) 
    { 
     var theSubscriber = subscriber; 
     try 
     { 
      if (subscriber.IsNew && bytesLeftToNextFrame < bytesRec) 
      { 
       //start from the index where the new frame starts 
       await theSubscriber.WriteAsync(bytes, bytesLeftToNextFrame, bytesRec - bytesLeftToNextFrame); 
       subscriber.IsNew = false; 
      } 
      else 
      { 
       //send everything, since we've already in streaming mode 
       await theSubscriber.WriteAsync(bytes, 0, bytesRec); 
      } 
     } 
     catch 
     { 
      Startup.Subscribers.Remove(theSubscriber); 
     } 
    } 

    //TODO: check if the current frame is done 
    // then parse the next header and reset the counter. 
} 
+0

Tìm thấy trình đọc siêu dữ liệu FLV đơn giản: http://johndyer.name/flash-flv-meta-reader-in-net-c/ – jgauffin

1

Tôi không phải là chuyên gia về trực tuyến, nhưng có vẻ như bạn nên đóng dòng sau đó tất cả dữ liệu sẽ được ghi

await theSubscriber.WriteAsync(bytes, 0, bytesRec); 

Giống như đề cập trong WebAPI StreamContent vs PushStreamContent

{ 
    // After save we close the stream to signal that we are done writing. 
    xDoc.Save(stream); 
    stream.Close(); 
} 
+0

Vấn đề là đây là một dòng sống và tôi muốn giữ nó sống khi người ta mới kết nối –

-2

Tôi thích mã này vì nó chứng tỏ một LỖI CƠ BẢN khi giao dịch với lập trình async

while (true) 
{ 

} 

này là một vòng lặp được đồng bộ hóa, mà vòng thân càng nhanh càng tốt .. mỗi giây nó có thể thực hiện hàng ngàn lần (tùy thuộc vào phần mềm availabe và tài nguyên phần cứng)

await theSubscriber.WriteAsync(bytes, 0, bytesRec); 

đây là một lệnh async (nếu đó là không đủ rõ ràng) mà thực hiện trong một chủ đề khác nhau (trong khi vòng lặp đại diện cho việc thực hiện luồng chính)

bây giờ ... để làm cho vòng lặp while đợi đến lệnh async chúng tôi sử dụng await ... âm thanh tốt (hoặc vòng lặp while khác sẽ thực thi hàng nghìn lần, thực hiện vô số các lệnh async)

NHƯNG vì vòng lặp (thuê bao) cần để truyền tải dòng cho tất cả các thuê bao simulatanly nó được stucked bởi các từ khóa đang chờ đợi

đó là lý do RELOAD/thuê bao MỚI Freeze toàn bộ điều (kết nối mới = người đăng ký mới)

kết luận: toàn bộ vòng lặp phải nằm trong Công việc. Tác vụ cần đợi cho đến khi máy chủ gửi luồng tới tất cả người đăng ký. CHỈ THÌ nó nên tiếp tục vòng lặp while với ContinueWith (đó là lý do tại sao nó được gọi như thế, phải không?)

do đó ...lệnh write cần để thực thi mà không khóa chờ đợi

theSubscriber.WriteAsync 

vòng lặp foreach nên sử dụng một công việc mà tiếp tục với vòng lặp while sau khi nó được thực hiện

+0

một vòng lặp được đồng bộ hóa? Bạn đang nói về cái gì vậy? Đó là một vòng lặp. giai đoạn. Những gì bạn làm trong đó là điều quan trọng. Anh ta sử dụng 'await' trong vòng lặp, do đó nó hoàn toàn hợp lệ. – jgauffin

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