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

2021-04-15 10:30:19 浏览数 (1)

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

1、界面简洁,支持窗体大小控制;

2、功能按钮通过右键给出菜单;

3、播放流地址、缓存设置、OSD叠加功能等放到配置文件中;

4、最主要的是低延迟播放;

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

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

对比下改造前的页面:

接下来介绍改造过程:

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

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

播放功能实现:

代码语言:javascript复制
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();
                }
            }
        }
 

截图功能实现:

代码语言:javascript复制
/// <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);
        }
 

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

配置参数如下:

代码语言:javascript复制
<?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类实现如下:

代码语言:javascript复制
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("创建配置文件时发生异常!n"   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;
            }
        }
    }

0 人点赞