Rx:同时下载文件列表,保留“FIFO”顺序

时间:2018-01-19 10:02:26

标签: c# concurrency reactive-programming system.reactive

我正在编写自己的(简单)HLS实现,为此,我需要下载MPEG传输流视频文件列表:

现在,我有这个,它有效:

public async Task<IObservable<(int Current, int Total, Stream Stream)>> FetchVideoSegments()
{
    var playlist = await GetPlaylist();

    var total = playlist.PlaylistItems.Count();

    return playlist.PlaylistItems.ToObservable()
        //=> Get the video file path, relative to the current playlist URI
        .Select(item => item.Uri)
        //=> Convert the relative URI to an absolute one
        .Select(MakeRelativeAbsoluteUrl)
        //=> Download the video transport file, sequentially
        .Select(uri => Observable.Defer(() => DownloadVideoSegment(uri)))
        .Concat()
        //=> Return the progress info tuple with the video file stream
        .Select((stream, index) => (index, total, stream));
}

按顺序通知订户,一次一个流。

通常,在下载文件时,2-3的并发性通常是理想的。我想将它添加到我的可观察管道中,但是我没有看到任何有效的方法来保持原始URI在发出的流中的插入顺序。

拿这个:

return playlist.PlaylistItems.ToObservable()
    //=> Get the video file path, relative to the current playlist URI
    .Select(item => item.Uri)
    //=> Convert the relative URI to an absolute one
    .Select(MakeRelativeAbsoluteUrl)
    //=> Download the video transport file
    .Select(uri => Observable.Defer(() => DownloadVideoSegment(uri)))
    //=> Limit concurrent requests to a reasonable number
    .Merge(FetchSegmentsMaxConcurrency)
    //=> Return the progress info tuple with the video file stream
    .Select((stream, index) => (index, total, stream));

请注意,.Concat()已替换为.Merge(maxConcurrency)

这是天真的解决方案,当然它不起作用:视频流以非确定性顺序发出。

实现这一目标的规范方法是什么?我应该保留另一个通过可观察管道保留的“索引”值吗?

1 个答案:

答案 0 :(得分:0)

这样的事情会是你想要的吗?当然,将char替换为DownloadVideoSegment返回的任何内容。

var random = new Random();

Observable
    .Range(65, 26)
    .Select(char.ConvertFromUtf32)
    .Select((letter, index) =>
        Observable
            .FromAsync(() =>
                Task.Delay(random.Next(5000))
                    .ContinueWith(t => (letter, index))))
    .Merge()
    .Do(tuple => Console.WriteLine($"{tuple.letter}:{tuple.index}"))
    .Buffer(Observable.Never<Unit>())
    .Subscribe(tuples =>
        Console.WriteLine($"{string.Join(",", tuples.OrderBy(t => t.index).Select(t => t.letter))}"));