You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

ASP.NET MVC视频分片响应及转码相关技术问题咨询

Handling Video Streaming & Transcoding in ASP.NET MVC

Hey there, let's tackle each of your questions one by one to get your video playback working smoothly:

1. Implementing the LoadVideo Action for Remote Video Streaming

To serve a remote video to your HTML5 <video> tag with chunked support, you need to handle HTTP Range requests—this is what allows browsers to request only parts of the video at a time, enabling smooth playback without loading the entire file upfront. Here's a complete implementation:

public async Task<ActionResult> LoadVideo()
{
    var remoteVideoUrl = "http://another-server.com/123.mp4";
    using var httpClient = new HttpClient();

    // Check if the browser sent a Range header for chunked requests
    var rangeHeader = Request.Headers["Range"];
    if (!string.IsNullOrEmpty(rangeHeader))
    {
        // Parse the range format: bytes=start-end
        var rangeParts = rangeHeader.Replace("bytes=", "").Split('-');
        if (long.TryParse(rangeParts[0], out var start))
        {
            // Get total length of the remote video first
            var headResponse = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, remoteVideoUrl));
            var contentLength = long.Parse(headResponse.Content.Headers.ContentLength.ToString());
            
            var end = rangeParts.Length > 1 && long.TryParse(rangeParts[1], out var parsedEnd) 
                ? Math.Min(parsedEnd, contentLength - 1) 
                : contentLength - 1;

            // Request only the needed byte range from the remote server
            var request = new HttpRequestMessage(HttpMethod.Get, remoteVideoUrl);
            request.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue(start, end);
            
            var response = await httpClient.SendAsync(request);
            var stream = await response.Content.ReadAsStreamAsync();

            // Set headers required for partial content response
            Response.StatusCode = (int)HttpStatusCode.PartialContent;
            Response.Headers.Add("Accept-Ranges", "bytes");
            Response.Headers.Add("Content-Range", $"bytes {start}-{end}/{contentLength}");
            
            return new FileStreamResult(stream, "video/mp4")
            {
                FileDownloadName = "video.mp4"
            };
        }
    }

    // Fallback: send the entire video if no range header is present
    var fullStream = await httpClient.GetStreamAsync(remoteVideoUrl);
    return new FileStreamResult(fullStream, "video/mp4");
}

This code:

  • Avoids downloading the entire remote video to your server by fetching only requested chunks
  • Returns the 206 Partial Content status code required for HTML5 video streaming
  • Supports breakpoint resuming if the user pauses and resumes playback

2. Using the HTML5 Video Tag

The HTML code you provided will work perfectly once your LoadVideo action supports range requests. Just double-check the src points to your controller and action path (e.g., /Home/LoadVideo if your controller is HomeController).

3. Streaming Large Videos in Chunks

The core solution is supporting HTTP Range requests, as shown in the first example. Browsers automatically send these headers when loading large videos, so your action just needs to:

  1. Parse the requested byte range from the Range header
  2. Fetch only that specific range from the remote server
  3. Return the partial content with the correct status code and headers

This approach keeps memory usage low on your server and lets the browser load chunks incrementally as needed.

4. Transcoding 720P Video to 480P

To transcode video, you'll need a video processing tool—FFmpeg is the industry standard. You can call it via command line or use a .NET wrapper like FFmpeg.AutoGen. Here's a simplified implementation:

private async Task<string> TranscodeTo480P(string inputUrl)
{
    var tempInputPath = Path.GetTempFileName() + ".mp4";
    var tempOutputPath = Path.GetTempFileName() + ".mp4";

    // Download remote 720P video to a temporary file
    using var httpClient = new HttpClient();
    using var inputStream = await httpClient.GetStreamAsync(inputUrl);
    using var fileStream = new FileStream(tempInputPath, FileMode.Create);
    await inputStream.CopyToAsync(fileStream);

    // FFmpeg command to transcode to 480P (adjust parameters for quality/speed)
    var ffmpegPath = Path.Combine(HttpContext.Server.MapPath("~/Bin"), "ffmpeg.exe");
    var arguments = $"-i \"{tempInputPath}\" -vf scale=-2:480 -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k \"{tempOutputPath}\"";

    // Execute FFmpeg process
    var process = new ProcessStartInfo
    {
        FileName = ffmpegPath,
        Arguments = arguments,
        RedirectStandardOutput = true,
        RedirectStandardError = true,
        UseShellExecute = false,
        CreateNoWindow = true
    };

    using var ffmpegProcess = Process.Start(process);
    await ffmpegProcess.WaitForExitAsync();

    // Clean up temporary input file
    System.IO.File.Delete(tempInputPath);

    return tempOutputPath;
}

// Updated LoadVideo action to serve transcoded 480P video
public async Task<ActionResult> LoadVideo()
{
    var remoteVideoUrl = "http://another-server.com/123.mp4";
    var transcodedPath = await TranscodeTo480P(remoteVideoUrl);
    var fileInfo = new FileInfo(transcodedPath);

    // Handle range requests for the transcoded file
    var rangeHeader = Request.Headers["Range"];
    if (!string.IsNullOrEmpty(rangeHeader))
    {
        var rangeParts = rangeHeader.Replace("bytes=", "").Split('-');
        if (long.TryParse(rangeParts[0], out var start))
        {
            var end = rangeParts.Length > 1 && long.TryParse(rangeParts[1], out var parsedEnd) 
                ? Math.Min(parsedEnd, fileInfo.Length - 1) 
                : fileInfo.Length - 1;

            var stream = new FileStream(transcodedPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            stream.Seek(start, SeekOrigin.Begin);
            var contentLength = end - start + 1;

            Response.StatusCode = (int)HttpStatusCode.PartialContent;
            Response.Headers.Add("Accept-Ranges", "bytes");
            Response.Headers.Add("Content-Range", $"bytes {start}-{end}/{fileInfo.Length}");

            return new FileStreamResult(stream, "video/mp4")
            {
                FileDownloadName = "video_480p.mp4"
            };
        }
    }

    // Serve full transcoded file if no range request
    return File(transcodedPath, "video/mp4", "video_480p.mp4");
}

Pro Tip: Add a background service to clean up old temporary transcoded files to avoid disk space bloat.

5. Transcoding to .m3u8 (HLS Format)

Absolutely! HLS (HTTP Live Streaming) uses a .m3u8 playlist file that points to small .ts video chunks, which is great for adaptive bitrate streaming. Here's how to implement it with FFmpeg:

Step 1: Generate HLS Playlist & Chunks

Use this FFmpeg command to convert your video to HLS format:

ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k -hls_time 10 -hls_list_size 0 -hls_segment_filename "chunk_%03d.ts" output.m3u8
  • -hls_time 10: Creates 10-second video chunks
  • -hls_list_size 0: Keeps all chunks in the playlist

Step 2: ASP.NET MVC Actions for HLS

public async Task<ActionResult> LoadHlsVideo()
{
    var remoteVideoUrl = "http://another-server.com/123.mp4";
    var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
    Directory.CreateDirectory(tempDir);

    var inputPath = Path.Combine(tempDir, "input.mp4");
    var outputM3u8Path = Path.Combine(tempDir, "output.m3u8");

    // Download remote video
    using var httpClient = new HttpClient();
    using var inputStream = await httpClient.GetStreamAsync(remoteVideoUrl);
    using var fileStream = new FileStream(inputPath, FileMode.Create);
    await inputStream.CopyToAsync(fileStream);

    // Run FFmpeg to generate HLS
    var ffmpegPath = Path.Combine(HttpContext.Server.MapPath("~/Bin"), "ffmpeg.exe");
    var arguments = $"-i \"{inputPath}\" -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k -hls_time 10 -hls_list_size 0 -hls_segment_filename \"{tempDir}/chunk_%03d.ts\" \"{outputM3u8Path}\"";

    var process = new ProcessStartInfo
    {
        FileName = ffmpegPath,
        Arguments = arguments,
        RedirectStandardOutput = true,
        RedirectStandardError = true,
        UseShellExecute = false,
        CreateNoWindow = true
    };

    using var ffmpegProcess = Process.Start(process);
    await ffmpegProcess.WaitForExitAsync();

    // Update chunk URLs in the playlist to point to our chunk-serving action
    var m3u8Content = await System.IO.File.ReadAllTextAsync(outputM3u8Path);
    var updatedM3u8 = m3u8Content.Replace("chunk_", $"/Home/GetHlsChunk?dir={tempDir}&chunk=");
    
    return Content(updatedM3u8, "application/vnd.apple.mpegurl");
}

// Action to serve individual HLS chunks
public ActionResult GetHlsChunk(string dir, string chunk)
{
    var chunkPath = Path.Combine(dir, chunk);
    return File(chunkPath, "video/MP2T");
}

Step 3: Frontend for HLS

Most modern browsers support HLS natively, but add hls.js for broader compatibility:

<video width="400" controls>
    <source src="/Home/LoadHlsVideo" type="application/vnd.apple.mpegurl">
    Your browser does not support HTML5 video.
</video>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<script>
    var video = document.querySelector('video');
    if (Hls.isSupported()) {
        var hls = new Hls();
        hls.loadSource('/Home/LoadHlsVideo');
        hls.attachMedia(video);
    }
</script>

内容的提问来源于stack exchange,提问作者eslamirad

火山引擎 最新活动