Andy's Blog » opera, opera 插件开发, NP_GetMIMEDescription, opera插件开发, NPP_GetMIMEDescription, opera 插件 开发, opera 开发插件 » 自己动手编写Opera插件

自己动手编写Opera插件

作者:未知

\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 [email protected]";
   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}

Incoming search terms:

Tags: 插件, Opera, 开发

本文地址: http://www.21andy.com/new/20080528/1133.html

1 评论 to “PHP开源CMS之MODx”

  1. didadi 于 2008-06-13 23:17:13 发表:

    高手,不过昨天 Opera 9.50 都发布了。