Видео HTML5 - обработчик ashx - seek

Я пытаюсь использовать обработчик ashx в качестве источника видео HTML5. Я могу этого добиться, но не могу двигаться дальше по видео, которое уже буферизовано.

Я могу видеть на вкладке сети, используя стандартный источник MP4, который при поиске вперед создает другой запрос, но с использованием обработчика этого не происходит.

Некоторые видео имеют размер более 1 ГБ.

Вот что мне нужно, чтобы видео работали до сих пор:

public void ProcessRequest(HttpContext context)
{
    context.Response.Buffer = false;
    context.Response.ContentType = "video/mp4";
    FileInfo file = new FileInfo(path);
    int len = (int)file.Length, bytes;
    context.Response.AppendHeader("content-length", len.ToString());
    byte[] buffer = new byte[1024];
    Stream outStream = context.Response.OutputStream;
    using (Stream stream = File.OpenRead(path))
    {
        while (len > 0 && (bytes = stream.Read(buffer, 0, buffer.Length)) > 0)
        {
            outStream.Write(buffer, 0, bytes);
            len -= bytes;
        }
    }
}

Взято из сообщения Марка Гравелла Лучший способ потоковой передачи файлов в ASP. NET:

<video id="video-player" class="video-js vjs-default-skin" controls
     preload="auto" poster="MY_VIDEO_POSTER.jpg"
     data-setup="{}">
     <source src="/Handlers/VideoHandler.ashx" type='video/mp4'>
     <p class="vjs-no-js">
        To view this video please enable JavaScript, and consider upgrading to a web browser that
        <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a>
     </p>
    </video>

используя video.js, но то же самое происходит, если я вызываю обработчик прямо в браузере.

Как я могу исправить эту проблему? Скорее всего, я упустил что-то простое.

ОБНОВЛЕНИЕ

Посмотрев дальше, я теперь знаю, что мне нужны «диапазоны приема».

У меня теперь есть

context.Response.ContentType = "video/mp4";

var request = HttpContext.Current.Request;
FileInfo file = new FileInfo(filePath);
var responseLength = (int)file.Length;
var buffer = new byte[1024];
var startIndex = 0;

if (request.Headers["Range"] != null)
{
    var match = Regex.Match(request.Headers["Range"], @"bytes=(\d*)-(\d*)");
    startIndex = Parse<int>(match.Groups[1].Value);
    responseLength = (Parse<int?>(match.Groups[2].Value) + 1 ?? responseLength) - startIndex;
    context.Response.StatusCode = (int)HttpStatusCode.PartialContent;
    context.Response.Headers["Content-Range"] = "bytes " + startIndex + "-" + (startIndex + responseLength - 1) + "/" + responseLength;
}

context.Response.Headers["Accept-Ranges"] = "bytes";
context.Response.Headers["Content-Length"] = responseLength.ToString();
context.Response.Cache.SetCacheability(HttpCacheability.Public);

context.Response.Buffer = false;

с помощью Поддерживая возобновляемые загрузки HTTP через обработчик ASHX?, но теперь запись вызывает у меня проблемы с ошибкой

HttpException не было обработано пользовательским кодом. Удаленный хост закрыл соединение. Код ошибки - 0x800704CD.

Я пытался:

using (Stream stream = File.OpenRead(filePath))
{

    stream.Seek(startIndex, SeekOrigin.Begin);
    while ((responseLength - startIndex) > 0 && (bytes = stream.Read(buffer, 0, buffer.Length)) > 0)
    {
        response.OutputStream.Write(buffer, 0, bytes);
        responseLength -= bytes;
    }
}

а также

using (Stream stream = File.OpenRead(filePath))
{
    stream.Seek(startIndex, SeekOrigin.Begin);
    int bufferLength = buffer.Length, bytesRead;
    while (responseLength > bufferLength && (bytesRead = stream.Read(buffer, 0, bufferLength)) > 0)
    {
        response.OutputStream.Write(buffer, 0, bytesRead);
        responseLength -= bytesRead;
    }
    while (responseLength > 0 && (bytesRead = stream.Read(buffer, 0, responseLength)) > 0)
    {
        response.OutputStream.Write(buffer, 0, bytesRead);
        responseLength -= bytesRead;
    }
}

Как я могу исправить эту проблему?


person BenG    schedule 23.06.2014    source источник


Ответы (2)


Эта последняя ошибка была связана с Chrome.

Следующее отлично работает в Firefox.

Если кто-то знает, почему Chrome отменяет соединение, поделитесь.

    public void ProcessRequest(HttpContext context)
    {
        context.Response.AddHeader("content-disposition", "filename=" + Path.GetFileName(filePath));
        context.Response.ContentType = "video/mp4";

        var request = HttpContext.Current.Request;
        FileInfo file = new FileInfo(filePath);
        var responseLength = (int)file.Length;
        var buffer = new byte[512];
        var startIndex = 0;

        if (request.Headers["Range"] != null )
        {
            var match = Regex.Match(request.Headers["Range"], @"bytes=(\d*)-(\d*)");
            startIndex = Parse<int>(match.Groups[1].Value);
            responseLength = (Parse<int?>(match.Groups[2].Value) + 1 ?? responseLength) - startIndex;
            context.Response.StatusCode = (int)HttpStatusCode.PartialContent;
            context.Response.Headers["Content-Range"] = "bytes " + startIndex + "-" + (startIndex + responseLength - 1) + "/" + responseLength;
        }

        context.Response.Headers["Accept-Ranges"] = "bytes";
        context.Response.Headers["Content-Length"] = responseLength.ToString();
        context.Response.Cache.SetCacheability(HttpCacheability.Public);

        context.Response.BufferOutput = false;
        FileStream fileStram = new FileStream(filePath, FileMode.Open, FileAccess.Read);
        using (fileStram)
        {
            fileStram.Seek(startIndex, SeekOrigin.Begin);
            int bytesRead = fileStram.Read(buffer, 0, buffer.Length);
            while (bytesRead > 0)
            {
                context.Response.OutputStream.Write(buffer, 0, bytesRead);
                bytesRead = fileStram.Read(buffer, 0, buffer.Length);
            }
        }
    }

    public static T Parse<T>(object value)
    {
        // Convert value to string to allow conversion from types like float to int
        // converter.IsValid only works since .NET4 but still returns invalid values for a few cases like NULL for Unit and not respecting locale for date validation
        try { return (T)System.ComponentModel.TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(value.ToString()); }
        catch (Exception) { return default(T); }
    }
person BenG    schedule 25.06.2014
comment
Не уверен, почему, но похоже, что это результат отключения буферизации. Но если вы исправите ошибку в заголовке Content-Range, где она должна "/" + file.Length вместо "/" + responseLength, поскольку при возобновлении загрузки на полпути это разные значения, тогда она будет работать в Chrome даже с отключенной буферизацией, вызывая ошибку. Вам нужно добавить ETAG, если вы хотите, чтобы он работал в IE. - person Michael; 17.07.2014

Нет необходимости открывать и читать файл. Я последовал этому трюку, просто добавив "Accept-Ranges" заголовок и переведя файл context.Response.TransmitFile. Теперь видео воспроизводится и ищется в каждом браузере, например Chrome, Firefox.

 if (fi.Extension == ".mp4")
 {
    //Reset and set response headers. The Accept-Ranges Bytes header is important to allow resuming videos.
    context.Response.AddHeader("Accept-Ranges", "bytes");
 }
 docRepo.AddDocumentView(FileDetail.ID);
 context.Response.TransmitFile(HttpContext.Current.Server.MapPath(General.PathConstants.Documents + Id + fi.Extension));
person Sheeraz ALI    schedule 03.12.2020