Avrohom Yisroel
Avrohom Yisroel

Reputation: 9440

How do I stream a video file using ASP.NET MVC?

I am developing a web site where people can access audio and video files. I have the code for downloading the files, which consists of two action methods as follows...

public ActionResult GetAudioFile(int id) {
  return GetFile(id, true);
}

public ActionResult GetVideoFile(int id) {
  return GetFile(id, false);
}

private ActionResult GetFile(int id, bool isAudio) {
  // Code to get info about the file, etc omitted for clarity
  string downloadFileName = // full path to audio/video file
  byte[] bytes = GetFileBytes(fileName); // also omitted
  return File(bytes, (isAudio ? "audio/mpeg" : "video/mp4"), downloadFileName + (isAudio ? ".mp3" : ".mp4"));
}

These both work fine, and I can download either type of file.

I now want to add two Razor views, one for listening to the audio file, and one for viewing the video. I did the following on the audio view, and it works fine...

<audio src='@Url.Action("GetAudioFile", "Search", new {ID = @Model.ID})'
               controls preload="auto"></audio>

I then tried to do the same for the video view...

<video src='@Url.Action("GetVideoFile", "Search", new {ID = @Model.ID})' 
               controls preload="auto" width="640" height="368"></video>

However, this gives a System.OutOfMemoryException when I try to run it. The videos average around 400-500Mb each.

I then tried using the Response.TransmitFile method as follows...

  Response.ContentType = "video/mp4";
  Response.AddHeader("content-disposition", "attachment; filename=myvideo.mp4"));
  Response.TransmitFile(fileName);
  Response.End();
  return null;

...but that doesn't work. In Chrome, I can see the message "Resource interpreted as Document but transferred with MIME type video/mp4" in the console, and in FireFox, I get the same message in the video control.

Anyone any idea how I can fix this? Ideally, I would like to stream the file, so that it starts playing as soon as the first bytes reach the user, rather than having them wait until the file is completely downloaded.

I've tried a couple of Javascript video players, but had any success with those either.

Update I'm wondering if it's not the controller action that's the problem at all, as I tried pointing my web browser directly at the video file, and I got what looked like an audio player, but without a video pane, and nothing happens when I click the play button. Not sure if that helps.

Upvotes: 13

Views: 38345

Answers (3)

Haris
Haris

Reputation: 1

Controller

Imports System.Web.Mvc

Namespace Controllers
    Public Class HomeController
        Inherits Controller

        Function Index() As ActionResult
            Return View()
        End Function

        Sub movie(id As Integer) 'As ActionResult

            Dim fm As String = "D:\Downloads\Rhoma Irama  Riza Umami - Suratan (Official Music Video).mp4"

            If id = 2 Then fm = "D:\Downloads\JERA riza umami   lagu dangdut   YouTube.mp4"
            If id = 3 Then fm = "D:\Downloads\FTV Trans TV CINTANYA ANAK HITS KEKINIAN RIDWAN GHANI.mp4"

            Dim fi As New IO.FileInfo(fm)
            Dim fs As IO.FileStream = IO.File.OpenRead(fm)
            Dim buff_size As Integer = 1048576 '1Mb buffering
            Dim buff(buff_size) As Byte, max_l As Integer = fs.Length - 1

            If Not Request.ServerVariables("HTTP_RANGE") Is Nothing Then

                Dim r() As String = Request.ServerVariables("HTTP_RANGE").Split("=")
                Dim s() As String = r(1).Split("-")
                Dim bs As Integer = 0, be As Integer = 0, l As Integer = 0

                If IsNumeric(s(0)) Then bs = s(0)
                If IsNumeric(s(1)) Then be = s(1)

                If bs >= 0 And bs <= max_l Then

                    Response.StatusCode = 206
                    Response.ContentType = "video/" & fi.Extension
                    Response.AddHeader("Accept-Ranges", "0-" & max_l)
                    Response.AddHeader("Content-Range", "bytes " & bs & "-" & bs + buff_size & "/" & max_l)
                    Response.AddHeader("Content-Length", buff_size)

                    fs.Position = bs
                    l = fs.Read(buff, 0, buff.Length)
                    If l > 0 Then Response.OutputStream.Write(buff, 0, buff.Length)

                End If

            End If

        End Sub
    End Class
End Namespace

VIEWS (I use Razor)

<body>
    <button onclick="video1.src='@Url.Action("movie", "home", New With {.id = 1})'">1</button>
    <button onclick="video1.src='@Url.Action("movie", "home", New With {.id = 2})'">2</button>
    <button onclick="video1.src='@Url.Action("movie", "home", New With {.id = 3})'">3</button>
    <video controls autoplay="autoplay" id="video1" width="920">
        <source src="@Url.Action("movie", "home", New With {.id = 1})" type="video/mp4">
        Your browser does not support HTML5 video.
    </video>  
</body>

Upvotes: -1

WasiF
WasiF

Reputation: 28847

The possible way to play a video from Database / PC

<div height="240" width="320" controls>
      <video>
             <source src="@Url.Content(Model.VideoTable.VideoPath)" type='video/mp4' />
      </video>
</div>

source property needs to get url not the string,

so @Url.Content converts the video-path as Url

Upvotes: -1

bstenzel
bstenzel

Reputation: 1241

The File method of MvcController returns a FileContentResult. The latter does not stream the content. As far as I'm aware, MvcController does not support streaming at all.

You may want to try ApiController and PushStreamContent. The latter gives the possibility to asynchronously write to the output stream via a callback method. You'll need to work with actual streams in your solution for that. Working with byte[] will always load the whole file content into memory.

See a detailed tutorial here:Asynchronously streaming video with ASP.NET Web API

Upvotes: 6

Related Questions