如何用C#控制YouTube的Flash播放器?

6
我的目标是制作一个开源的YouTube播放器,可以通过全局媒体键进行控制。全局键问题已经解决,但是YouTube播放器和我的Windows Forms应用程序之间的通信由于某些原因无法正常工作。
到目前为止,这就是我所拥有的。
private AxShockwaveFlashObjects.AxShockwaveFlash player;
player.movie = "http://youtube.googleapis.com/v/9bZkp7q19f0"
...
private void playBtn_Click(object sender, EventArgs e)
{
    player.CallFunction("<invoke name=\"playVideo\" returntype=\"xml\"></invoke>");
}

不幸的是,这会返回:

"Error HRESULT E_FAIL has been returned from a call to a COM component."

我错过了什么?我应该加载不同的URL吗?
文档指出,YouTube播放器使用ExternalInterface类来从JavaScript或AS3控制它,因此它应该可以与c#一起工作


更新:


用于嵌入播放器的方法:http://www.youtube.com/watch?v=kg-z8JfOIKw

还尝试在WebBrowser控件中使用JavaScript-API,但没有成功(播放器不响应JavaScript命令,甚至尝试将WebBrowser.url设置为工作演示,但只能通过简单的嵌入式对象版本触发onYouTubePlayerReady())。

我认为可能有一些安全问题我没有注意到,不知道。


更新2:


亲测可行,详见我的答案

你的应用程序需要使用所有CPU或64位处理器吗? - Paul Farry
尝试了两种方法,都没有成功... 你设法让它工作了吗? - Stefan Rogin
1
抱歉,这只是我在使用COM时尝试过的一些方法。我想你也将其设置为x86并尝试了吧。同时启动ProcessMonitor(Sysinternals)并跟踪它正在查询的注册表键。可能会提供一些指针。 - Paul Farry
我会尝试,虽然我没有跟踪注册表的经验,但我应该寻找什么? - Stefan Rogin
这是我找到的内容: reg:“HKLM \ SOFTWARE \ Microsoft \ CTF \ KnownClasses” RegOpenKey名称未找到。 - Stefan Rogin
抱歉,我没有实际的答案。会有很多密钥滚动显示,你需要浏览它们,找到与ShockwaveFlash(被拒绝、未找到等)相关的内容,然后跟进处理。很抱歉我不能更具体,因为我不知道完整的答案。通常情况下,这是由于注册不正确或32/64位安装不匹配引起的。 - Paul Farry
4个回答

4
似乎您正在尝试使用Adobe Flash作为界面,并将某些变量传回C#,以下是一个示例:
在Flash中创建一个按钮,Actionscript如下:
on (press) {
    fscommand("Yo","dude");
}

然后在Visual Studio中,您只需要添加COM对象引用:Shockwave Flash Object。

接下来将嵌入设置为true;

然后在Visual Studio中,您应该能够进入属性;查找fscommand。 fscommand将允许您从Flash电影中实际连接值。

AxShockwaveFlashObjects._IShockwaveFlashEvents_FSCommandEvent 

这段代码是用于收集信息的;然后只需使用e.commande.arg来让收集到的内容执行某些操作。

接下来,将此代码添加到事件处理程序中即可。

lbl_Result.Text="The "+e.args.ToString()+" "+e.command.ToString()+" was clicked";

咔嚓一声,数据就从Flash传输到Visual Studio了,不需要使用任何麻烦复杂的套接字。

此外,如果你在Visual Studio中使用Flash,关键是确保其“embed”属性设置为true。这将保留Flash对象内部的所有路径引用,避免调用错误路径。

如果没有更多关于你的目标/错误的详细信息,我无法为你提供帮助,希望这些内容能对你有所帮助。前面的部分应该可以展示最佳的将Shockwave嵌入Visual Studio的方法。

请确保添加正确的引用:

  1. 在项目中打开“解决方案资源管理器”
  2. 右击选择“添加引用”
  3. 进入“COM对象”

找到正确的对象;

COM Objects:
Shockwave ActiveX
Flash Accessibility
Flash Broker
Shockwave Flash

希望这有所帮助。
听起来你没有正确地嵌入它,所以你不能调用它。如果我有些错误;或者这就是你的意思:
如果你有困难,Ryk之前发过一篇帖子,介绍了一种嵌入YouTube视频的方法:
<% MyYoutubeUtils.ShowEmebddedVideo("<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/gtNlQodFMi8&hl=en&fs=1"></param><param name="allowFullScreen" value="true"></param><embed src="http://www.youtube.com/v/gtNlQodFMi8&hl=en&fs=1" type="application/x-shockwave-flash" allowfullscreen="true" width="425" height="344"></embed></object>") %>

或者...
public static string ShowEmbeddedVideo(string youtubeObject)
{
    var xdoc = XDocument.Parse(youtubeObject);
    var returnObject = string.Format("<object type=\"{0}\" data=\{1}\"><param name=\"movie\" value=\"{1}\" />",
        xdoc.Root.Element("embed").Attribute("type").Value,
        xdoc.Root.Element("embed").Attribute("src").Value);
    return returnObject;
}

您可以在此处找到该线程:https://stackoverflow.com/questions/2547101/purify-embedding-youtube-videos-method-in-c-sharp 非常抱歉,如果我的帖子显得有些零散;但我无法确定是引用、变量、方法还是嵌入式造成了您的困难。真心希望这可以帮到您;或者请给我更多细节,我会相应地调整我的回答。
C# 到 ActionScript 的通信:
import flash.external.ExternalInterface;
ExternalInterface.addCallback("loadAndPlayVideo", null, loadAndPlayVideo);
function loadAndPlayVideo(uri:String):void
{
       videoPlayer.contentPath = uri;
}

然后在C#中;添加ActiveX控件的实例,并将内容添加到构造函数中。

private AxShockwaveFlash flashPlayer;
public FLVPlayer ()
{

      // Add Error Handling; to condense I left out.
      flashPlayer.LoadMovie(0, Application.StartupPath + "\\player.swf");
}

fileDialog = new OpenFileDialog();
fileDialog.Filter = "*.flv|*.flv";
fileDialog.Title = "Select a Flash Video File...";
fileDialog.Multiselect = false;
fileDialog.RestoreDirectory = true;

if (fileDialog.ShowDialog() == DialogResult.OK)
{
     flashPlayer.CallFunction("<invoke" + " name=\"loadAndPlayVideo\" returntype=\"xml">       <arguements><string>" + fileDialog.FileName + "</string></arguements></invoke>");
}

ActionScript与C#之间的通信:

import flash.external.ExternalInterface;
ExternalInterface.call("ResizePlayer", videoPlayer.metadata.width, videoPlayer.metadata.height);

flashPlayer.FlashCall += new _IShockwaveFlashEvents_FlashCallEventHandler(flashPlayer_FlashCall);

然后XML应该出现:
<invoke name="ResizePlayer" returntype="xml">
     <arguements>
            <number> 320 </number>
            <number> 240 </number>
     </arguments>
</invoke>

然后在事件处理程序中解析XML并在本地调用C#函数。

 XmlDocument document = new XmlDocument();
    document.LoadXML(e.request);
    XmlNodeList list = document.GetElementsByTagName("arguements");
    ResizePlayer(Convert.ToInt32(list[0].FirstChild.InnerText),   Convert.ToInt32(list[0].ChildNodes[1].InnerText));

现在它们互相传递数据。这只是一个基本的例子,但通过使用ActionScript通信,您不应该有任何问题使用本机API。
希望这更有帮助。您可以通过为重复使用创建实用程序类来扩展该想法。显然,上面的代码有一些限制,但希望它能指引您朝着正确的方向前进。那是您试图去的方向吗?还是我仍然误解了您的意思?
在ActionScript 3中创建新的Flash动画。然后在初始第一帧上应用以下内容:
Security.allowDomain("www.youtube.com");
var my_player:Object;
var my_loader:Loader = new Loader();

my_loader.load(new URLRequest("http://www.youtube.com/apiplayer?version=3"))
my_loader.contentLoaderInfo.addEventListener(Event.INIT, onLoaderInit);

function onLoaderInit(e:Event):void{
addChild(my_loader);
my_player = my_loader.content;
my_player.addEventListener("onReady", onPlayerReady); 
} 

function onPlayerReady(e:Event):void{
my_player.setSize(640,360);
my_player.loadVideoById("_OBlgSz8sSM",0);
} 

那么这个脚本到底在做什么?它利用原生API并使用ActionScript通信。下面我将分解每一行。

Security.allowDomain("www.youtube.com");

没有这条线,YouTube将无法与该对象进行交互。
var my_player:Object;

你不能简单地把电影加载到电影中,因此我们将创建一个变量对象。您必须加载一个特殊的.swf文件,其中包含对这些代码的访问权限。以下内容实现了这一点。因此,您可以访问API。

var my_loader:Loader = new Loader();
my_loader.load(new URLRequest("http://www.youtube.com/apiplayer?version=3")); 

我们现在按照谷歌API的文档进行引用。
my_loader.contentLoaderInfo.addEventListener(Event.INIT, onLoaderInit);

但是,为了实际使用我们的对象,我们需要等待它完全初始化。因此,事件监听器将等待,以便我们知道何时可以向其发送命令。

onLoaderInit函数将在初始化时触发。然后它的第一个任务是使my_loader显示列表,以便视频出现。

addChild(my_loader);将加载一个,my_player = my_loader.content;将存储一个引用,以便轻松访问该对象。

尽管它已经被初始化,您仍需要等待更久...... 使用my_player.addEventListener("onReady", onPlayerReady);等待并监听这些自定义事件。这将允许稍后的函数来处理。

现在播放器已准备好进行基本配置。

function onPlayerReady(e:Event):void{
my_player.setSize(640,360);
} 

上述函数开始进行基本操作。然后最后一行my_player.loadVideoById("_OBlgSz8sSM",0);引用了特定的视频。

接下来,在您的舞台上,您可以创建两个按钮并应用:

play_btn.addEventListener(MouseEvent.CLICK, playVid); 
function playVid(e:MouseEvent):void { 
my_player.playVideo(); 
} 
pause_btn.addEventListener(MouseEvent.CLICK, pauseVid); 
function pauseVid(e:MouseEvent):void { 
my_player.pauseVideo();
}

这将为您提供播放和暂停功能。您可以使用一些其他项目:

loadVideoById() 
cueVideoById() 
playVideo() 
pauseVideo() 
stopVideo() 
mute()
unMute()

请记住在完全初始化之前,这些内容不能被使用或调用。但是使用此方法可以让您布局目标并在两者之间传递变量以进行操作。

希望这有所帮助。


嵌入不是问题,YouTube视频可以播放,一切都很好,但我想从C#控制播放器,这时就出现了错误。 这就是我的进展:http://www.youtube.com/watch?v=kg-z8JfOIKw - Stefan Rogin
似乎fscommand可以用于从Flash向C#发送数据,但不能反向传输。我需要一个双向通信系统,这样我就可以向Flash播放器发送命令。 如果可能的话,我想使用原始的YouTube播放器API,因为这样更容易维护。 - Stefan Rogin
在这种情况下,您可以使用ActionScript通信文件;这将允许您使用本机API。但是然后传递给C#,以便可以在本地处理。我会编辑我的回复以反映我的意思;我会添加到底部。 - Greg
是的,这就是我正在尝试的,但我认为YouTube播放器已经嵌入了它,并使用它与JavaScript或其他AS3/AS2脚本进行通信,但flashPlayer.CallFunction给我一个错误。 - Stefan Rogin
我所概述的方法应该可行;YouTube实际上使用了一个在运行时加载的特殊文件。然后你只需要使用Loader类,就可以播放任何YouTube视频。我会在我的编辑帖子中添加... - Greg

2

经过多次尝试和思考,我找到了一个解决方案:

似乎当Flash无法理解所请求的Flash调用时,Error HRESULT E_FAIL...会发生。此外,为使YouTube外部API正常工作,需要启用js api

player.movie = "http://www.youtube.com/v/VIDEO_ID?version=3&enablejsapi=1"

如我在问题中所说,整个程序都是开源的,因此您可以在Bitbucket上找到完整的代码。
非常感谢任何建议、建议或合作者。

完整的解决方案:

以下是嵌入和交互YouTube播放器或任何其他Flash对象的完整指南。

在按照视频教程后,将Flash播放器的FlashCall事件设置为处理flash-> c#交互的函数(在我的示例中为YTplayer_FlashCall

生成的`InitializeComponent()`应为:

...
this.YTplayer = new AxShockwaveFlashObjects.AxShockwaveFlash();
this.YTplayer.Name = "YTplayer";
this.YTplayer.Enabled = true;
this.YTplayer.OcxState = ((System.Windows.Forms.AxHost.State)(resources.GetObject("YTplayer.OcxState")));
this.YTplayer.FlashCall += new AxShockwaveFlashObjects._IShockwaveFlashEvents_FlashCallEventHandler(this.YTplayer_FlashCall);
...

FlashCall事件处理程序

private void YTplayer_FlashCall(object sender, AxShockwaveFlashObjects._IShockwaveFlashEvents_FlashCallEvent e)
{
    Console.Write("YTplayer_FlashCall: raw: "+e.request.ToString()+"\r\n");
    // message is in xml format so we need to parse it
    XmlDocument document = new XmlDocument();
    document.LoadXml(e.request);
    // get attributes to see which command flash is trying to call
    XmlAttributeCollection attributes = document.FirstChild.Attributes;
    String command = attributes.Item(0).InnerText;
    // get parameters
    XmlNodeList list = document.GetElementsByTagName("arguments");
    List<string> listS = new List<string>();
    foreach (XmlNode l in list){
        listS.Add(l.InnerText);
    }
    Console.Write("YTplayer_FlashCall: \"" + command.ToString() + "(" + string.Join(",", listS) + ")\r\n");
    // Interpret command
    switch (command)
    {
        case "onYouTubePlayerReady": YTready(listS[0]); break;
        case "YTStateChange": YTStateChange(listS[0]); break;
        case "YTError": YTStateError(listS[0]);  break;
        default: Console.Write("YTplayer_FlashCall: (unknownCommand)\r\n"); break;
    }
}

这将解决flash->c#通信的问题。

调用flash外部函数(c#->flash):

private string YTplayer_CallFlash(string ytFunction){
    string flashXMLrequest = "";
    string response="";
    string flashFunction="";
    List<string> flashFunctionArgs = new List<string>();

    Regex func2xml = new Regex(@"([a-z][a-z0-9]*)(\(([^)]*)\))?", RegexOptions.Compiled | RegexOptions.IgnoreCase);
    Match fmatch = func2xml.Match(ytFunction);

    if(fmatch.Captures.Count != 1){
        Console.Write("bad function request string");
        return "";
    }

    flashFunction=fmatch.Groups[1].Value.ToString();
    flashXMLrequest = "<invoke name=\"" + flashFunction + "\" returntype=\"xml\">";
    if (fmatch.Groups[3].Value.Length > 0)
    {
        flashFunctionArgs = pars*emphasized text*eDelimitedString(fmatch.Groups[3].Value);
        if (flashFunctionArgs.Count > 0)
        {
            flashXMLrequest += "<arguments><string>";
            flashXMLrequest += string.Join("</string><string>", flashFunctionArgs);
            flashXMLrequest += "</string></arguments>";
        }
    }
    flashXMLrequest += "</invoke>";

    try
    {
        Console.Write("YTplayer_CallFlash: \"" + flashXMLrequest + "\"\r\n");
        response = YTplayer.CallFunction(flashXMLrequest);                
        Console.Write("YTplayer_CallFlash_response: \"" + response + "\"\r\n");
    }
    catch
    {
        Console.Write("YTplayer_CallFlash: error \"" + flashXMLrequest + "\"\r\n");
    }

    return response;
}

private static List<string> parseDelimitedString (string arguments, char delim = ',')
{
    bool inQuotes = false;
    bool inNonQuotes = false;
    int whiteSpaceCount = 0;

    List<string> strings = new List<string>();

    StringBuilder sb = new StringBuilder();
    foreach (char c in arguments)
    {
        if (c == '\'' || c == '"')
        {
            if (!inQuotes)
                inQuotes = true;
            else
                inQuotes = false;

            whiteSpaceCount = 0;
        }else if (c == delim)
        {
            if (!inQuotes)
            {
                if (whiteSpaceCount > 0 && inQuotes)
                {
                    sb.Remove(sb.Length - whiteSpaceCount, whiteSpaceCount);
                    inNonQuotes = false;
                }
                strings.Add(sb.Replace("'", string.Empty).Replace("\"", string.Empty).ToString());
                sb.Remove(0, sb.Length);                       
            }
            else
            {
                sb.Append(c);
            }
            whiteSpaceCount = 0;
        }
        else if (char.IsWhiteSpace(c))
        {                    
            if (inNonQuotes || inQuotes)
            {
                sb.Append(c);
                whiteSpaceCount++;
            }
        }
        else
        {
            if (!inQuotes) inNonQuotes = true;
            sb.Append(c);
            whiteSpaceCount = 0;
        }
    }
    strings.Add(sb.Replace("'", string.Empty).Replace("\"", string.Empty).ToString());


    return strings;
}

添加Youtube事件处理程序:

private void YTready(string playerID)
{
    YTState = true;
    //start eventHandlers
    YTplayer_CallFlash("addEventListener(\"onStateChange\",\"YTStateChange\")");
    YTplayer_CallFlash("addEventListener(\"onError\",\"YTError\")");
}
private void YTStateChange(string YTplayState)
{
    switch (int.Parse(YTplayState))
    {
        case -1: playState = false; break; //not started yet
        case 1: playState = true; break; //playing
        case 2: playState = false; break; //paused
        //case 3: ; break; //buffering
        case 0: playState = false; if (!loopFile) mediaNext(); else YTplayer_CallFlash("seekTo(0)"); break; //ended
    }
}
private void YTStateError(string error)
{
    Console.Write("YTplayer_error: "+error+"\r\n");
}

使用示例:

YTplayer_CallFlash("playVideo()");
YTplayer_CallFlash("pauseVideo()");
YTplayer_CallFlash("loadVideoById(KuNQgln6TL0)");
string currentVideoId = YTplayer_CallFlash("getPlaylist()");
string currentDuration = YTplayer_CallFlash("getDuration()");

函数 YTplayer_CallFlashYTplayer_FlashCall 可以用于任何 flash-C# 通信,只需进行少量调整,例如 YTplayer_CallFlash 的开关(命令)。


很高兴你找到了解决方案;我的帖子有帮助吗? - Greg
不幸的是,在回答发布之前,我已经通过在线研究获得了这些信息,而且它们并不特定适用于我的问题。 参见 stackoverflow 回答指南 中的“回答问题”一节。 - Stefan Rogin
你写成了 parceDelimitedString 而不是 parseDelimitedString - Toalp
我收到了错误HRESULT E_FAIL...,尽管我已经启用了enablejsapi。看起来YouTube不再支持Flash了。:( - T N
不知道,我已经4年没有测试过了,而且Google似乎已经弃用了flash+jsapi,你应该检查一下它是否在浏览器中能够工作。 - Stefan Rogin

2
我会从确保JavaScript与您的Flash应用程序之间可以通信开始。
请确认您已经在嵌入式中设置了allowScriptAccess =“sameDomain”(来自http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/external/ExternalInterface.html#includeExamplesSummary)。
您应该验证html->flash是否有效;然后是C-> html;并逐渐升级到C-> you-tube-component。 目前,在C和you-tube-component之间存在很多潜在的故障点,因此很难同时解决所有问题。

html->flash可以在Chrome/Firefox/IE9中正常工作,但无法在WebBrowser组件中使用,这就是我想通过AxShockwaveFlash组件嵌入的原因。现在我将尝试获取c->flash并查看其效果。c->youtube失败了。 - Stefan Rogin
如果你能够进行C->JS和JS->Flash的转换,那么似乎对于YouTube组件存在一些误解。我非常确定,如果你无法从Flash控制其Flash组件,那么现在肯定已经有人注意到了 :) 祝你好运! - dtudury
WebBrowser的JS无法正常工作,因此webBrowserJS->Flash不可行(问题在问题描述中有所描述)。同时,C#->webBrowserJS->flash也是错误的选择。 我已经成功让c#->flash工作,并将很快发布完整的响应。 - Stefan Rogin
啊,太糟糕了。我的专长完全在你的光谱的闪存端,所以我的反馈主要是为了弄清楚我是否可以提供任何支持...抱歉我不能更有帮助。 - dtudury
不用担心,所有的反馈都很有帮助 :) - Stefan Rogin

0

我知道了,已经想通了,看下面我的答案 :) : "为了让YouTube外部API正常工作,需要启用 js api 。" - Stefan Rogin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接