自己动手编写Opera插件

自己动手编写Opera插件<<转http://www.21andy.com/blog/20080528/1133.html>>

 

 

/documentclass[a4paper]{article}
/usepackage{CJK}
/usepackage{color}
/usepackage{listings}
/usepackage[dvips, CJKbookmarks, colorlinks]{hyperref}

/lstset{language=C++,keywordstyle=/color{blue}/textbf}
/setlength{/parindent}{2em}
/renewcommand{/baselinestretch}{1.5}

/begin{document}
/begin{CJK*}{GBK}{song}

/title{自己动手编写Opera插件}
/author{陶冶 /and E-Mail: taoyi5178-at-gmail.com}

/maketitle

前段时间看到Firefox有了新版本,便去下了个试试,没想到很不爽,老崩溃。最后终于在N次死掉之后,在忍无可忍之下,怀着十分郁闷的心情把Firefox干干净净地请出来了我的电脑。经过这么长时间新老版本的使用,在我的印象中Firefox并没有传说中的那么神奇。当然也许我的运气比较糟点。不过世界上很多事情大致都是一个道理,看问题的角度不同就会有不同的答案,从相同或相似角度看到的总是多了那么一些共性,而我或许正好因为运气的关系偏离了原本相似的角度。清除了Firefox后,自然还是得找个替代品。Lynx也许很多人没用过,一个基于字符终端的浏览器,速度挺快,资源占用也比较少,不过缺点就是没有那么图形界面下来得有声有色,如果有时候想看看带点颜色的东西那是没有办法的。很久以前就听说过Opera了,不过还没有真正见识过,这次正好可以试试。

 

从Opera官方网站下载了最新的9.10 for linux版本,大概才9M多点。没想到这么小的东西安装后就什么都有了,除了是浏览器外,还集成了邮件客户端、RSS阅读器、下载管理器、日记本、联系人管理器等等。总之,经过一段时间的使用后,感觉相当爽,页面美观,速度很快,支持类似VIM的查找方式......但是也有让人遗憾的地方,就是没法播放在线音乐和视频,这点很难让人接受。虽然官方网站上有好几种常用的媒体文件播放插件,尽管我没有一一试过,但从其中之一MPlayer的插件来看,却并不是专门为之打造的,都是Firefox、Mozilla的插件。就以MPlayer插件为例,我从/url{http://mplayerplug-in.sourceforge.net}下载了最新的3.35版,折腾了半天就硬是没装上。最终在台湾的一个网站上看到一篇文章后,下载了3.25版并修改了源代码后才装上的。虽然是装上了,也可以播放在线音乐了,可就是出不来界面,一块灰白色的方框,死活没有播放、停止等按钮,很是不便。虽然还有其它选择,比如gxine插件等,但是不是这样依赖就是那么依赖,很烦。最终觉得应该自己写个才行,方便自己,于是在找了些资料后,自己开始研究Opera的插件。

 

从官方的网站上看到的有关Opera插件API里只有一段话,大概意思就是说Opera实现了Netscape4的插件接口,其它就没啦。后来想来,一些最新的Firefox插件不能用在Opera上的原因,恐怕就在这里,因为最新的Firefox插件基本上都使用的是XPCOM模型实现的,有的根本就没有提供MAC、Netscape老的接口,也难怪了。

 

Netscape插件模型包括在Mozilla的Gecko-sdk开发包中。Gecko-sdk是Mozilla、Firefox的插件开发包,它提供了跨平台的插件开发接口,包括XPCOM之类的东西(这东西好象很复杂,不太清楚)。Netscape插件模型提供了两套接口,一套接口是浏览器端实现,供插件实例调用的以NPN开头的函数;一套是插件自身实现,供浏览器调用的以NPP开头的函数。浏览器端的接口我们不用管,主要看看插件需要实现的接口就可以了。下面看看几个主要的函数:

 

/begin{lstlisting}[frame=trbl]{}
char *NP_GetMIMEDescription(void)
/end{lstlisting}

 

这个函数是浏览器用以查看插件MIME头的描述,相当于是插件的注册函数,浏览器调用此函数返回的MIME描述信息后,就将之接收到的与该函数返回的与MIME信息相同的数据交由插件来处理。

 

/begin{lstlisting}[frame=trbl]{}
NPError NP_GetValue (
void *future,
NPPVariable variable,
void *value)
/end{lstlisting}

 

 

浏览器通过这个函数取得有关插件自身的信息,诸如插件的名称、插件的描述等。

 

/begin{lstlisting}[frame=trbl]{}
NPError NP_Initialize(
NPNetscapeFuncs* nsFuncs,
NPPluginFuncs* pluginFuncs)
/end{lstlisting}

 

浏览器调用这个函数实现插件的初始化。NPNetscapeFuncs结构是浏览器实现的函数表,NPPluginFuncs是插件实现的函数表,插件实现的接口需要在这里注册以供浏览器调用。

 

/begin{lstlisting}[frame=trbl]{}
NPError NP_Shutdown(void)
/end{lstlisting}

 

这个函数在浏览器不需要插件时调用以释放插件。

 

/begin{lstlisting}[frame=trbl]{}
NPError NPP_New(
NPMIMEType pluginType,
NPP instance,
uint16 mode,
int16 argc,
char *argn[],
char *argv[],
NPSavedData *saved)
/end{lstlisting}

 

这个函数在浏览器创建插件时调用,其中argc是参数的个数,argn是参数名称,argv是参数值,这三个参数相当重要。当然这样说是不会明白的,举个例子:

 

/begin{lstlisting}[frame=trbl]{}
<embed
type="application/base-plugin"
width=600
height=40
src="http://www.music.com/test.mp3">
/end{lstlisting}

 

 

如果我们的插件注册成处理application/base-plugin类型的数据,则以上HTML代码对应的NPP/_New函数的参数为:

 

/begin{lstlisting}[frame=trbl]{}
argc = 4
argn = {"type", "width", "height", "src"}
argv = {"application/base-plugin", "600", "40",
"http://www.music.com/test.mp3"}
/end{lstlisting}

/begin{lstlisting}[frame=trbl]{}
NPError NPP_Destroy(NPP instance, NPSavedData **save)
/end{lstlisting}

 

浏览器调用这个函数删除由NPP/_New创建的插件实例。


/begin{lstlisting}[frame=trbl]{}
void NP_Shutdown(void)
/end{lstlisting}

这个函数最后调用,以释放插件。

好了,几个主要的接口函数就介绍到这里,下面再来回顾一下插件代码的执行顺序:

/begin{enumerate}
/item 浏览器调用NP/_Initialize传递自身实现接口并注册插件实现接口;
/item 浏览器调用NPP/_New创建实例;
/item 运行插件逻辑;
/item 调用NPP/_Destroy删除插件实例;
/item 调用NP/_Shutdown释放插件;
/end{enumerate}


大体代码框架如下:

/begin{lstlisting}[frame=trbl]{}
char *NP_GetMIMEDescription(void)
{
return "video/mpeg: mpeg, mpg, mpe: MPEG animation;"
}

NPError NP_GetValue (
void *future,
NPPVariable variable,
void *value)
{
NPError err = NPERR_NO_ERROR;
switch (variable) {
case NPPVpluginNameString:
   *((char **)value) = "mplayer plugin";
   break;
case NPPVpluginDescriptionString:
   *((char **)value) =
     "writen by taoyi5178@gmail.com";
   break;
default:
   err = NPERR_GENERIC_ERROR;
}
return err;
}

NPError NP_Initialize(
NPNetscapeFuncs* nsFuncs,
NPPluginFuncs* pluginFuncs)
{
pluginFuncs->size = sizeof(NPPluginFuncs);
pluginFuncs->newq = NPP_New;
... ...

return NPERR_NO_ERROR;
}

NPError NPP_New(
NPMIMEType pluginType,
NPP instance,
uint16 mode,
int16 argc,
char *argn[],
char *argv[],
NPSavedData *saved)
{
... ...
}

NPError NPP_Destroy(
NPP instance,
NPSavedData **save)
{
return NPERR_NO_ERROR;
}

NPError NP_Shutdown(void)
{
return NPERR_NO_ERROR;
}

/end{lstlisting}


下面是一个调用xine实现播放常见媒体文件的Opera插件代码。将该程序编译成共享库,然后将编译后的共享库复制到/$OPERA/_INSTALL/_PATH/lib/opera/plugins目录下,即完成插件的注册。之后在浏览网页时,凡是遇到相应MIME类型的数据,都将调用xine播放器来播放。稍微想多做点工作的话,还可以将每一个播放地址保存成播放列表,以备下次使用。当然你也可以使用其它你喜欢的播放器来完成这个工作。

/begin{lstlisting}{}
#include <stdio.h>
#include <npapi.h>
#include <npupp.h>
#ifdef LOG
#include <stdarg.h>
#endif

void xprintf(const char *format, ...);
NPError NPP_Private_New(
NPMIMEType pluginType,
NPP instance,
uint16 mode,
int16 argc,
char *argn[],
char *argv[],
NPSavedData *saved);
NPError NPP_Private_Destroy(NPP instance, NPSavedData **save);


//static NPNetscapeFuncs __nsFuncs;

//--------------------------------------------------------------
void xprintf(const char *format, ...)
{
#ifdef LOG
FILE *log_fd = NULL;
va_list argp;
if (!log_fd)
{
log_fd = fopen("/tmp/plug-in.log", "a+");
if (log_fd)
{
setvbuf(log_fd, NULL, _IONBF, 0);
fprintf(log_fd, "/n*************/n/n");
}
}

va_start(argp, format);
if (log_fd)
vfprintf(log_fd, format, argp);

va_end(argp);
#endif
}

//--------------------------------------------------------------
char *NP_GetMIMEDescription(void) {

xprintf("NPP_GetMIMEDescription:/n");

return "video/mpeg: mpeg, mpg, mpe: MPEG animation;"
        "video/x-mpeg: mpeg, mpg, mpe: MPEG animation;"
        "audio/mpeg2: mp2: MPEG audio;"
        "audio/x-mpeg2: mp2: MPEG audio;"
        "audio/mpeg3: mp3: MPEG audio;"
        "audio/x-mpeg3: mp3: MPEG audio;"
        "audio/mpeg: mpa,abs,mpega: MPEG audio;"
        "audio/x-mpeg: mpa,abs,mpega: MPEG audio;"
        "video/quicktime: mov,qt: Quicktime animation;"
        "video/x-quicktime: mov,qt: Quicktime animation;"
        "video/msvideo: avi: AVI animation;"
        "video/x-msvideo: avi: AVI animation;"
        "application/x-mplayer2: asf,asx,asp: mplayer2;"
        "video/x-ms-asf-plugin: asf,asx,asp: mms animation;"
        /*"audio/x-pn-realaudio-plugin: rpm: Real audio;"*/
        "audio/x-ogg: ogg,ogm: OGG Media;"
        "audio/x-scpls: pls: MPEG audio"
;
}

//--------------------------------------------------------------
NPError NP_GetValue (
void *future,
NPPVariable variable,
void *value)
{
NPError err = NPERR_NO_ERROR;
xprintf("NP_GetValue: variable=%d/n", variable);

switch (variable) {
case NPPVpluginNameString:
   *((char **)value) = "Opera audio/video plugin";
   break;
case NPPVpluginDescriptionString:
   *((char **)value) = "writen by taoyi5178-at-gmail.com";
   break;
default:
   err = NPERR_GENERIC_ERROR;
}
return err;
}

//--------------------------------------------------------------
NPError NP_Initialize(
NPNetscapeFuncs* nsFuncs,
NPPluginFuncs* pluginFuncs)
{
xprintf("NP_Initialize/n");

pluginFuncs->size   = sizeof(NPPluginFuncs);
pluginFuncs->newp   = NPP_Private_New;
pluginFuncs->destroy = NPP_Private_Destroy;

return NPERR_NO_ERROR;
}

//--------------------------------------------------------------
NPError NPP_Private_New(
NPMIMEType pluginType,
NPP instance,
uint16 mode,
int16 argc,
char *argn[],
char *argv[],
NPSavedData *saved)
{
int i;
int ns_major, ns_minor, pluginMajor, pluginMinor;
pid_t pid;

xprintf("NPP_New/n");

for (i = 0; i < argc; i++)
{
xprintf("%s=%s/n", argn[i], argv[i]);
if (strcasecmp(argn[i], "src") == 0)
{
xprintf("execute xine to play...");
if ((pid = fork()) < 0)
   xprintf("fork error");
else if (pid == 0)
   execlp("xine", "xine", argv[i], NULL);
}
}

//xprintf("UserAgent: %s/n", __nsFuncs.uagent(instance));

return NPERR_NO_ERROR;
}

//--------------------------------------------------------------
NPError NPP_Private_Destroy(NPP instance, NPSavedData **save)
{
xprintf("NPP_Destroy/n");
return NPERR_NO_ERROR;
}

//--------------------------------------------------------------
NPError NP_Shutdown(void)
{
xprintf("NP_Shutdown/n");
return NPERR_NO_ERROR;
}
/end{lstlisting}

OK,到此结束。看不懂的或不明白的,请自行参考后面的参考资料。


/newpage
/begin{thebibliography}{99}
/bibitem{pa} /href{http://web.archive.org/web/20040408173931/devedge.netscape.com/library/manuals/2002/plugin/1.0/pluginTOC.html}{Netscape Gecko Plug-in API Reference}
/bibitem{pa} /href{http://mplayerplug-in.sourceforge.net/}{mplayer plug-in ---- embedded video player for mozilla}
/end{thebibliography}

/end{CJK*}
/end{document}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值