ASP.NET 使用FFmpeg实现MP4转换M3U8

868 阅读3分钟

未安装FFmpeg的,请查看安装教程 >>  

MP4转M3U8结果演示,推荐使用VLC Media Player进行M3U8播放测试

image.png

示例源码

1、前台调用

 
<!DOCTYPE html>
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>MP4视频转换为M3U8格式</title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <p>视频ID:<asp:TextBox ID="txtCourseName" runat="server"></asp:TextBox></p>
            <p>本地视频:<asp:FileUpload ID="upVideoFile" runat="server" /></p>
            <p>片段时长:<asp:TextBox ID="txtVideoLength" runat="server" Text="5" TextMode="Number"></asp:TextBox></p>
            <p><asp:Button ID="btnSave" runat="server" Text="开始转换" OnClick="btnSave_Click" /></p>
            <p><asp:Literal ID="ltrTimer" runat="server"></asp:Literal></p>
        </div>
    </form>
</body>
</html>
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
 
namespace AspNet_FFmpeg
{
    public partial class UpCourse : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
 
        }
 
        protected void btnSave_Click(object sender, EventArgs e)
        {
            try
            {
                this.ltrTimer.Text = DateTime.Now.ToString("hh:mm:ss:ms");
 
                if (this.txtCourseName.Text.Trim().Equals(""))
                {
                    Response.Write("<script>alert('请填写ID!')</script>");
                    return;
                }
 
                if (!this.upVideoFile.HasFile)
                {
                    Response.Write("<script>alert('请选择视频!')</script>");
                    return;
                }
 
                //上传文件的名称的命名规则是标题
                string fileName = this.txtCourseName.Text;
 
                string filePathTemp = "upload_files/" + this.txtCourseName.Text.Trim();
                string fpath = Path.Combine(Request.PhysicalApplicationPath, filePathTemp);
 
                // 目录不存在,则创建目录
                if (!Directory.Exists(fpath))
                {
                    Directory.CreateDirectory(fpath);
                }
 
                //取出上传的视频的文件名,进而取出该文件的扩展名
                string sourceFileName = Path.GetFileName(upVideoFile.FileName);
                string extendName = sourceFileName.Substring(sourceFileName.LastIndexOf(".") + 1);
 
                //检测上传文件是否为MP4文件
                if (!extendName.ToLower().Equals("mp4"))
                {
                    Response.Write("<script>alert('请选择MP4格式视频!')</script>");
                    return;
                }
 
                //上传到服务器
                //上传后的文件名的命名规则是:标题+后缀  
                string fileNameTemp = fileName + "." + extendName;
                string videoUrl = Path.Combine(fpath, fileNameTemp);
                this.upVideoFile.SaveAs(videoUrl);
 
                //截取缩略图
                string imgUrl1 = Path.Combine(fpath, fileName + "01.jpg");
                FFmpegHelper.CatchImg(videoUrl, imgUrl1, 0);
 
                //截取缩略图
                string imgUrl2 = Path.Combine(fpath, fileName + "02.jpg");
                FFmpegHelper.CatchImg(videoUrl, imgUrl2, 1);
 
                //截取缩略图
                string imgUrl3 = Path.Combine(fpath, fileName + "03.jpg");
                FFmpegHelper.CatchImg(videoUrl, imgUrl3, 2);
 
                //检测是否已生成缩略图
                if ((!System.IO.File.Exists(imgUrl1)) || !System.IO.File.Exists(imgUrl2) || !System.IO.File.Exists(imgUrl3))
                {
                    //删除缩略图
                    System.IO.File.Delete(imgUrl1);
                    System.IO.File.Delete(imgUrl2);
                    System.IO.File.Delete(imgUrl3);
 
                    //删除MP4源文件
                    System.IO.File.Delete(videoUrl);
 
                    Response.Write("<script>alert('视频转换失败!')</script>");
                    return;
                }
 
                //进行视频转换
                string tsUrl = Path.Combine(fpath, fileName + ".ts");
                FFmpegHelper.VideoToTs(videoUrl, tsUrl);
 
                //检测是否已生成ts文件
                if (!System.IO.File.Exists(tsUrl))
                {
                    //删除缩略图
                    System.IO.File.Delete(imgUrl1);
                    System.IO.File.Delete(imgUrl2);
                    System.IO.File.Delete(imgUrl3);
 
                    //删除MP4源文件
                    System.IO.File.Delete(videoUrl);
 
                    Response.Write("<script>alert('视频转换失败!')</script>");
                    return;
                }
 
                //生成M3U8文件
                string m3u8Path = Path.Combine(fpath, fileName);
                int VideoLength = Convert.ToInt32(this.txtVideoLength.Text);
                FFmpegHelper.TsToM3u8(tsUrl, m3u8Path, VideoLength);
 
                //检测是否已生成M3U8文件
                if (!System.IO.File.Exists(m3u8Path + ".m3u8"))
                {
                    //删除缩略图
                    System.IO.File.Delete(imgUrl1);
                    System.IO.File.Delete(imgUrl2);
                    System.IO.File.Delete(imgUrl3);
 
                    //删除MP4源文件
                    System.IO.File.Delete(videoUrl);
                    //删除TS源文件
                    System.IO.File.Delete(tsUrl);
 
                    Response.Write("<script>alert('视频转换失败!')</script>");
                    return;
                }
 
                //删除MP4源文件
                System.IO.File.Delete(videoUrl);
                //删除TS源文件
                System.IO.File.Delete(tsUrl);
 
                this.ltrTimer.Text += (" - " + DateTime.Now.ToString("hh:mm:ss:ms"));
 
                Response.Write("<script>alert('视频转换完成!')</script>");
            }
            catch (Exception ex)
            {
                Response.Write("<script>alert('" + ex.Message + "')</script>");
                return;
            }
 
        }
    }
}

2、FFmpegHelper公共类

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Web;
 
namespace AspNet_FFmpeg
{
    public static class FFmpegHelper
    {
        //安装的ffmpeg的路径 写在配置文件的 你也可以直接写你的路径 D:\ffmpeg\bin\ffmpeg.exe
        //static string FFmpegPath = System.Configuration.ConfigurationManager.AppSettings["ffmepg"];
 
        /// <summary>
        /// 视频转码为ts文件
        /// </summary>
        /// <param name="videoUrl"></param>
        /// <param name="targetUrl"></param>
        public static void VideoToTs(string videoUrl, string targetUrl)
        {
            //视频转码指令
            string cmd = string.Format("ffmpeg -y -i \"{0}\" -vcodec copy -acodec copy -vbsf h264_mp4toannexb \"{1}\"", videoUrl, targetUrl);
            RunMyProcess(cmd);
        }
 
        /// <summary>
        /// 将ts文件转换为mu3u8文件
        /// </summary>
        /// <param name="tsUrl"></param>
        /// <param name="m3u8Url">这个路径不要带扩展名</param>
        /// <param name="videoLength">视频切片时长,默认5秒</param>
        public static void TsToM3u8(string tsUrl, string m3u8Url, int videoLength = 5)
        {
            //这里是关键点,一般平时切视频都是用FFmpeg -i  地址 -c这样,但是在服务器时,这样调用可能找不到ffmpeg的路径 所以这里直接用ffmpeg.exe来执行命令
            //string cmd = $@"{FFmpegPath} -i {tsUrl} -c copy -map 0 -f segment -segment_list {m3u8Url}.m3u8 -segment_time 5 {m3u8Url}%03d.ts";
            string cmd = string.Format("ffmpeg -i \"{0}\" -c copy -map 0 -f segment -segment_list \"{1}.m3u8\" -segment_time {2} \"{1}%03d.ts\"", tsUrl, m3u8Url, videoLength);
            RunMyProcess(cmd);
        }
 
        /// <summary>
        /// 生成MP4频的缩略图
        /// </summary>
        /// <param name="videoUrl">视频文件地址</param>
        /// <param name="imgUrl">视频缩略图地址</param>
        /// <param name="imgSize">宽和高参数,如:240*180</param>
        /// <param name="Second">ss后跟的时间单位为秒</param>
        /// <returns></returns>
        public static string CatchImg(string videoUrl,string imgUrl, int Second=8, string imgSize= "1280x720")
        {
            try
            {
                //转换文件格式的同时抓缩微图: 
                //ffmpeg - i "test.avi" - y - f image2 - ss 8 - t 0.001 - s 350x240 'test.jpg'
                //- ss后跟的时间单位为秒
 
                string cmd = string.Format("ffmpeg -i \"{0}\" -y -f image2 -ss {1} -t 0.001 -s {2} \"{3}\"", videoUrl, Second, imgSize, imgUrl);
                RunMyProcess(cmd);
 
                //注意:图片截取成功后,数据由内存缓存写到磁盘需要时间较长,大概在3,4秒甚至更长;
                //System.Threading.Thread.Sleep(500);
                if (System.IO.File.Exists(imgUrl))
                {
                    return imgUrl;
                }
                return "";
            }
            catch
            {
                return "";
            }
        }
 
        /// <summary>
        /// 执行cmd指令
        /// </summary>
        /// <param name="Parameters"></param>
        public static void RunMyProcess(string Parameters)
        {
            using (Process p = new Process())
            {
                try
                {
                    p.StartInfo.FileName = "cmd.exe";
                    p.StartInfo.UseShellExecute = false;//是否使用操作系统shell启动
                    p.StartInfo.RedirectStandardInput = true;//接受来自调用程序的输入信息
                    p.StartInfo.RedirectStandardOutput = true;//由调用程序获取输出信息
                    p.StartInfo.RedirectStandardError = true;//重定向标准错误输出
                    p.StartInfo.CreateNoWindow = false;//不创建进程窗口                                                
                    p.Start();//启动线程
                    p.StandardInput.WriteLine(Parameters + "&&exit"); //向cmd窗口发送输入信息
                    p.StandardInput.AutoFlush = true;
                    p.StandardInput.Close();
                    //获取cmd窗口的输出信息
                    string output = p.StandardError.ReadToEnd(); //可以输出output查看具体报错原因
 
                    p.WaitForExit();//等待完成
                    p.Close();//关闭进程
                    p.Dispose();//释放资源
 
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }
    }
}