• TSINGSEE青犀视频流媒体播放器EasyPlayer-RTMP定制窗体开发过程分享


    因为2020年年底的时候各大浏览器厂商逐渐开始摒弃FLASH,导致基于WEB的RTMP协议流播放被大家诟病,这时候客户端又逐渐被大家捡起来使用。这两天就有一个用户需要定制一个RTMP低延迟的播放器,需求如下:

    1、界面简洁,支持窗体大小控制;
    2、功能按钮通过右键给出菜单;
    3、播放流地址、缓存设置、OSD叠加功能等放到配置文件中;
    4、最主要的是低延迟播放;

    根据需求内容,我们打算用EasyPlayer-RTMP进行改造,因为EasyPlayer-RTMP底层是基于EasyRTMPClient做的低延迟播放器,EasyRTMPClient可以提供稳定的拉流,回调数据清晰,兼容H264和H265。

    我们先看下改造后的界面如下图:

    对比下改造前的页面:

     接下来介绍改造过程:
    1、将界面的配置项全部修改到配置文件中去,增加一个XML读写的类XMLOperate(尾部附加),如下,可以读写配置文件:

    2、将播放、截图、录像、OSD显示等功能做到右键菜单中,增加contextMenuStrip控件:

     播放功能实现:

    private void 播放ToolStripMenuItem_Click(object sender, EventArgs e)
            {
                switch (XMLOperate.RENDER_FORMAT)
                {
                    case "GDI":
                        RENDER_FORMAT = PlayerSdk.RENDER_FORMAT.DISPLAY_FORMAT_RGB24_GDI; break;
                    case "RGB565":
                        RENDER_FORMAT = PlayerSdk.RENDER_FORMAT.DISPLAY_FORMAT_RGB565; break;
                    case "YV12":
                        RENDER_FORMAT = PlayerSdk.RENDER_FORMAT.DISPLAY_FORMAT_YV12; break;
                    case "YUY2":
                        RENDER_FORMAT = PlayerSdk.RENDER_FORMAT.DISPLAY_FORMAT_YUY2; break;
                    default:
                        break;
                }
                var isPlay = false;
                if (this.播放ToolStripMenuItem.Text == "播放")
                { isPlay = true; }
                else
                { isPlay = false; }
               
                if (isPlay)
                {
                    string RTSPStreamURI = XMLOperate.PlayerURL;// "rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov";
                    channelID = PlayerSdk.EasyPlayer_OpenStream(RTSPStreamURI, this.panel1.Handle, RENDER_FORMAT, isTCP ? 1 : 0, "", "", callBack, IntPtr.Zero, isHardEncode);
                    if (channelID > 0)
                    {
                        PlayerSdk.EasyPlayer_SetFrameCache(channelID, 3);
                        this.播放ToolStripMenuItem.Text = "停止";
                        this.DecodeType.Enabled = false;
                    }
                }
                else
                {
                    int ret = PlayerSdk.EasyPlayer_CloseStream(channelID);
                    if (ret == 0)
                    {
                        this.播放ToolStripMenuItem.Text = "播放";
                        this.DecodeType.Enabled = true;
                        channelID = -1;
                        this.panel1.Refresh();
                    }
                }
            }
     
    

    截图功能实现:

    /// <summary>
            /// 截图.
            /// </summary>
            /// <param name="sender">The source of the event.</param>
            /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
            private void Snop_MenuItem_Click(object sender, EventArgs e)
            {
                if (channelID <= 0)
                    return;
                int ret = PlayerSdk.EasyPlayer_PicShot(channelID);
            }
     
    

    其它功能类似实现。最后我们来看下效果:

    配置参数如下:

    <?xml version="1.0" encoding="utf-8"?>
    <启动配置参数>
      <URL>rtmp://183.224.164.130:10085/hls/hhsx2</URL>
      <渲染模式>GDI</渲染模式>
      <硬解>false</硬解>
      <缓存值>3</缓存值>
      <校验KEY值>59615A67426F69576B5A7541306C74676E3651744A663478567778576F502B6C2F32566863336B3D</校验KEY值>
      <备用参数A>RTMPMediaPlayer</备用参数A>
      <备用参数B>这是EasyPlayer-RTMP-Win播放器的字幕叠加接口的效果!</备用参数B>
    </启动配置参数>
     
    

    XMLOperate类实现如下:

    class XMLOperate
        {
           /// <summary>
           /// 播放的URL
           /// </summary>
            public static string PlayerURL = "";
            /// <summary>
            /// 渲染模式
            /// </summary>
            public static string RENDER_FORMAT = "";
            /// <summary>
            /// 是否硬解
            /// </summary>
            public static string isHardEncode = "";
            /// <summary>
            /// 缓存帧数
            /// </summary>
            public static string CacheFream = "";
            /// <summary>
            /// 授权KEY
            /// </summary>
            public static string KEY = "";
            /// <summary>
            /// 窗体名称
            /// </summary>
            public static string BYCSA = "";
            /// <summary>
            /// OSD叠加内容
            /// </summary>
            public static string BYCSB = "";
            
            public static void InitAppsettings()
            {
                CreateAndInitParamFileAppSettings();
                ReadAppSettingParamsFromConfigFile();
            }
     
            private static void CreateAndInitParamFileAppSettings()
            {
                try
                {
                    //创建缺省文件
                    XmlDocument XmlDoc = new XmlDocument();
                    string FileContent = "<?xml version='1.0' encoding='utf-8' ?>";
                    FileContent += "<启动配置参数>";
                    FileContent += @"<URL>rtmp://183.224.164.130:10085/hls/df8</URL>";
                    FileContent += "<渲染模式>1</渲染模式>";
                    FileContent += "<硬解>false</硬解>";
                    FileContent += "<缓存值>3</缓存值>";
                    FileContent += "<校验KEY值>59615A67426F69576B5A7541306C74676E3651744A663478567778576F502B6C2F32566863336B3D</校验KEY值>";
                    
                    FileContent += "<备用参数A>500</备用参数A>";
                    FileContent += "<备用参数B>500</备用参数B>";
                    FileContent += "</启动配置参数>";
                    XmlDoc.LoadXml(FileContent);
                    //判断文件是否存在,如果不存在则创建
                    if (!System.IO.File.Exists("./EasyPlayerConfig.xml"))
                    {
                        //判断路径是否存在,如果不存在则创建
                        if (!System.IO.Directory.Exists(System.IO.Path.GetDirectoryName("./EasyPlayerConfig.xml")))
                            System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName("./EasyPlayerConfig.xml"));
                        //创建文件
                        System.IO.File.Create("./EasyPlayerConfig.xml").Dispose();
                        XmlDoc.Save("./EasyPlayerConfig.xml");
                    }
     
                }
                catch (Exception e)
                {
                    //写异常日志
                    throw new Exception("创建配置文件时发生异常!
    " + e.Message);
                }
            }
     
            private static bool ReadAppSettingParamsFromConfigFile()
            {
                string fileName = "./EasyPlayerConfig.xml";
                if (fileName == "")
                    return false;
                try
                {
                    string xPath = @"//启动配置参数";
                    XmlDocument XmlDoc = new XmlDocument();
                    XmlDoc.Load(fileName);
                    XmlDocumentFragment DocFrag = XmlDoc.CreateDocumentFragment();
                    XmlNode RootNode = XmlDoc.DocumentElement;
                    XmlNode ReadingNode = RootNode.SelectSingleNode(xPath);
                    if (Object.Equals(ReadingNode, null))
                    {
                    }
                    else
                    {
                        xPath = @"//启动配置参数//URL";
                        ReadingNode = RootNode.SelectSingleNode(xPath);
                        string path = ReadingNode != null ? ReadingNode.InnerText : @"";
                        try
                        {
                            PlayerURL = path;
                        }
                        catch
                        {
                            PlayerURL = "";
                        }
     
                        xPath = @"//启动配置参数//渲染模式";
                        ReadingNode = RootNode.SelectSingleNode(xPath);
                        path = ReadingNode != null ? ReadingNode.InnerText : @"1";
                        try
                        {
                            RENDER_FORMAT = path;
                        }
                        catch
                        {
                            RENDER_FORMAT = @"1";
                        }
     
                        xPath = @"//启动配置参数//硬解";
                        ReadingNode = RootNode.SelectSingleNode(xPath);
                        string nRet = ReadingNode != null ? ReadingNode.InnerText : "false";
                        try
                        {
     
                            isHardEncode = nRet;
     
                        }
                        catch
                        {
                            isHardEncode = "";
                        }
     
                        xPath = @"//启动配置参数//缓存值";
                        ReadingNode = RootNode.SelectSingleNode(xPath);
                        nRet = ReadingNode != null ? ReadingNode.InnerText : "2";
                        try
                        {
     
                            CacheFream = nRet;
     
                        }
                        catch
                        {
                            CacheFream = "2";
                        }
     
                        xPath = @"//启动配置参数//校验KEY值";
                        ReadingNode = RootNode.SelectSingleNode(xPath);
                        path = ReadingNode != null ? ReadingNode.InnerText : "false";
                        try
                        {
                            KEY = path;
                        }
                        catch
                        {
                            KEY = "";
                        }
     
                       
                        xPath = @"//启动配置参数//备用参数A";
                        ReadingNode = RootNode.SelectSingleNode(xPath);
                        path = ReadingNode != null ? ReadingNode.InnerText : "500";
                        try
                        {
                            BYCSA = path;
                        }
                        catch
                        {
                            BYCSA = "500";
                        }
     
                        xPath = @"//启动配置参数//备用参数B";
                        ReadingNode = RootNode.SelectSingleNode(xPath);
                        path = ReadingNode != null ? ReadingNode.InnerText : "500";
                        try
                        {
                            BYCSB = path;
                        }
                        catch
                        {
                            BYCSB = "500";
                        }
                    }
                    return true;
                }
                catch
                {
                    return false;
                }
            }
        }
    

      

      

     
  • 相关阅读:
    触发器
    新登录用户的次日成功的留存率
    获取薪水第二多的
    找到薪水比经理高的员工
    成绩排名
    exists 和 in
    sum+case 计数
    前N个员工的salary累计和
    员工的薪水按照salary进行按照1N的排名,相同salary并列
    洛谷2678 跳石头
  • 原文地址:https://www.cnblogs.com/TSINGSEE/p/14663628.html
Copyright © 2020-2023  润新知