Patch for Libjingle with GCC 4.2.4 on Ubuntu

It is a svn diff result, not a patch, actually.

So, what is Libjingle? Quoted from http://code.google.com/p/libjingle/:

Libjingle, the Google Talk Voice and P2P Interoperability Library, is a set of components we provide to interoperate with Google Talk's peer-to-peer file sharing and voice calling capabilities. The package includes source code for Google's implementation of Jingle and Jingle-Audio, two proposed extensions to the XMPP standard that are currently available in draft form.

You can check out the head revision of Libjingle from its svn repository using command:

svn checkout http://libjingle.googlecode.com/svn/trunk/ libjingle-read-only

Then ``./autogen.sh'' and ``make'' as we usually do for building a *nix software. You will find many errors during ``./autogen.sh'' and ``make''. To fix them, first, some LIBs should be installed:

sudo apt-get install build-essential libexpat1-dev libglib2.0-dev libogg-dev libssl-dev libasound2-dev libspeex-dev openssl libortp7-dev libmediastreamer0-dev libavcodec-dev

I am not very sure if these LIBs are enough. If you have some problem with this, please let me know.

Even if you have all of these LIBs installed, you will still get some errors such as:

../../talk/base/stringutils.h:272: error: extra qualification 'talk_base::Traits::' on member 'empty_str'
../../talk/base/base64.h:26: error: extra qualification ‘talk_base::Base64::’ on member ‘Base64Table’
../../talk/base/base64.h:27: error: extra qualification ‘talk_base::Base64::’ on member ‘DecodeTable’

So here is a patch for source code errors like this. IMPORTANT NOTE: gcc version 4.2.4 on Ubuntu 8.04, libortp7.

Index: talk/p2p/base/sessionmanager.h
===================================================================
--- talk/p2p/base/sessionmanager.h    (revision 7)
+++ talk/p2p/base/sessionmanager.h    (working copy)
@@ -156,7 +156,7 @@

   // Creates and returns an error message from the given components.  The
   // caller is responsible for deleting this.
-  buzz::XmlElement* SessionManager::CreateErrorMessage(
+  buzz::XmlElement* CreateErrorMessage(
       const buzz::XmlElement* stanza,
       const buzz::QName& name,
       const std::string& type,
Index: talk/session/phone/linphonemediaengine.cc
===================================================================
--- talk/session/phone/linphonemediaengine.cc    (revision 7)
+++ talk/session/phone/linphonemediaengine.cc    (working copy)
@@ -80,24 +80,24 @@
     }
#endif
#ifdef HAVE_SPEEX
-    if (i->name == speex_wb.mime_type && i->clockrate == speex_wb.clock_rate) {
-      rtp_profile_set_payload(&av_profile, i->id, &speex_wb);
-    } else if (i->name == speex_nb.mime_type && i->clockrate == speex_nb.clock_rate) {
-      rtp_profile_set_payload(&av_profile, i->id, &speex_nb);
+    if (i->name == payload_type_speex_wb.mime_type && i->clockrate == payload_type_speex_wb.clock_rate) {
+      rtp_profile_set_payload(&av_profile, i->id, &payload_type_speex_wb);
+    } else if (i->name == payload_type_speex_nb.mime_type && i->clockrate == payload_type_speex_nb.clock_rate) {
+      rtp_profile_set_payload(&av_profile, i->id, &payload_type_speex_nb);
     }
#endif

     if (i->id == 0)
-      rtp_profile_set_payload(&av_profile, 0, &pcmu8000);
+      rtp_profile_set_payload(&av_profile, 0, &payload_type_pcmu8000);

-    if (i->name == telephone_event.mime_type) {
-      rtp_profile_set_payload(&av_profile, i->id, &telephone_event);
+    if (i->name == payload_type_telephone_event.mime_type) {
+      rtp_profile_set_payload(&av_profile, i->id, &payload_type_telephone_event);
     }
    
     if (first) {
       LOG(LS_INFO) << "Using " << i->name << "/" << i->clockrate;
       pt_ = i->id;
-      audio_stream_ = audio_stream_start(&av_profile, 2000, "127.0.0.1", 3000, i->id, 250);
+      audio_stream_ = audio_stream_start(&av_profile, 2000, (char *)"127.0.0.1", 3000, i->id, 250);
       first = false;
     }
   }
@@ -106,7 +106,7 @@
     // We're being asked to set an empty list of codecs. This will only happen when
     // working with a buggy client; let's try PCMU.
      LOG(LS_WARNING) << "Received empty list of codces; using PCMU/8000";
-    audio_stream_ = audio_stream_start(&av_profile, 2000, "127.0.0.1", 3000, 0, 250);
+    audio_stream_ = audio_stream_start(&av_profile, 2000, (char *)"127.0.0.1", 3000, 0, 250);
   }
 
}
@@ -114,12 +114,12 @@
bool LinphoneMediaEngine::FindCodec(const Codec &c) {
   if (c.id == 0)
     return true;
-  if (c.name == telephone_event.mime_type)
+  if (c.name == payload_type_telephone_event.mime_type)
     return true;
#ifdef HAVE_SPEEX
-  if (c.name == speex_wb.mime_type && c.clockrate == speex_wb.clock_rate)
+  if (c.name == payload_type_speex_wb.mime_type && c.clockrate == payload_type_speex_wb.clock_rate)
     return true;
-  if (c.name == speex_nb.mime_type && c.clockrate == speex_nb.clock_rate)
+  if (c.name == payload_type_speex_nb.mime_type && c.clockrate == payload_type_speex_nb.clock_rate)
     return true;
#endif
#ifdef HAVE_ILBC
@@ -171,8 +171,8 @@
#ifdef HAVE_SPEEX
   ms_speex_codec_init();

-  codecs_.push_back(Codec(110, speex_wb.mime_type, speex_wb.clock_rate, 0, 1, 8));
-  codecs_.push_back(Codec(111, speex_nb.mime_type, speex_nb.clock_rate, 0, 1, 7));
+  codecs_.push_back(Codec(110, payload_type_speex_wb.mime_type, payload_type_speex_wb.clock_rate, 0, 1, 8));
+  codecs_.push_back(Codec(111, payload_type_speex_nb.mime_type, payload_type_speex_nb.clock_rate, 0, 1, 7));
  
#endif

@@ -181,8 +181,8 @@
   codecs_.push_back(Codec(102, payload_type_ilbc.mime_type, payload_type_ilbc.clock_rate, 0, 1, 4));
#endif

-  codecs_.push_back(Codec(0, pcmu8000.mime_type, pcmu8000.clock_rate, 0, 1, 2));
-  codecs_.push_back(Codec(101, telephone_event.mime_type, telephone_event.clock_rate, 0, 1, 1));
+  codecs_.push_back(Codec(0, payload_type_pcmu8000.mime_type, payload_type_pcmu8000.clock_rate, 0, 1, 2));
+  codecs_.push_back(Codec(101, payload_type_telephone_event.mime_type, payload_type_telephone_event.clock_rate, 0, 1, 1));
   return true;
}

Index: talk/xmpp/xmppclient.h
===================================================================
--- talk/xmpp/xmppclient.h    (revision 7)
+++ talk/xmpp/xmppclient.h    (working copy)
@@ -138,7 +138,7 @@
     }
   }

-  std::string XmppClient::GetStateName(int state) const {
+  std::string GetStateName(int state) const {
     switch (state) {
       case STATE_PRE_XMPP_LOGIN:      return "PRE_XMPP_LOGIN";
       case STATE_START_XMPP_LOGIN:  return "START_XMPP_LOGIN";
Index: talk/third_party/mediastreamer/msrtprecv.c
===================================================================
--- talk/third_party/mediastreamer/msrtprecv.c    (revision 7)
+++ talk/third_party/mediastreamer/msrtprecv.c    (working copy)
@@ -26,7 +26,7 @@
MSMessage *msgb_2_ms_message(mblk_t* mp){
     MSMessage *msg;
     MSBuffer *msbuf;
-    if (mp->b_datap->ref_count!=1) return NULL; /* cannot handle properly non-unique buffers*/
+    if (mp->b_datap->db_ref!=1) return NULL; /* cannot handle properly non-unique buffers*/
     /* create a MSBuffer using the mblk_t buffer */
     msg=ms_message_alloc();
     msbuf=ms_buffer_alloc(0);
@@ -120,7 +120,7 @@
         gint got=0;
         /* we are connected with queues (surely for video)*/
         /* use the sync system time to compute a timestamp */
-        PayloadType *pt=rtp_profile_get_payload(r->rtpsession->profile,r->rtpsession->payload_type);
+        PayloadType *pt=rtp_profile_get_payload(r->rtpsession->rcv.profile,r->rtpsession->rcv.telephone_events_pt);
         if (pt==NULL) {
             ms_warning("ms_rtp_recv_process(): NULL RtpPayload- skipping.");
             return;
Index: talk/third_party/mediastreamer/audiostream.c
===================================================================
--- talk/third_party/mediastreamer/audiostream.c    (revision 7)
+++ talk/third_party/mediastreamer/audiostream.c    (working copy)
@@ -112,7 +112,7 @@
             RtpSession **recvsend){
     RtpSession *rtpr;
     rtpr=rtp_session_new(RTP_SESSION_SENDRECV);
-    rtp_session_max_buf_size_set(rtpr,MAX_RTP_SIZE);
+    rtp_session_set_recv_buf_size(rtpr,MAX_RTP_SIZE);
     rtp_session_set_profile(rtpr,profile);
     rtp_session_set_local_addr(rtpr,get_local_addr_for(remip),locport);
     if (remport>0) rtp_session_set_remote_addr(rtpr,remip,remport);
@@ -133,7 +133,7 @@
     /* creates two rtp filters to recv send streams (remote part)*/
    
     rtps=rtp_session_new(RTP_SESSION_SENDONLY);
-    rtp_session_max_buf_size_set(rtps,MAX_RTP_SIZE);
+    rtp_session_set_recv_buf_size(rtps,MAX_RTP_SIZE);
     rtp_session_set_profile(rtps,profile);
#ifdef INET6
     rtp_session_set_local_addr(rtps,"::",locport+2);
@@ -147,7 +147,7 @@
     rtp_session_set_jitter_compensation(rtps,jitt_comp);
    
     rtpr=rtp_session_new(RTP_SESSION_RECVONLY);
-    rtp_session_max_buf_size_set(rtpr,MAX_RTP_SIZE);
+    rtp_session_set_recv_buf_size(rtpr,MAX_RTP_SIZE);
     rtp_session_set_profile(rtpr,profile);
#ifdef INET6
     rtp_session_set_local_addr(rtpr,"::",locport);
@@ -217,8 +217,8 @@
     ms_filter_set_property(stream->decoder,MS_FILTER_PROPERTY_FREQ,&pt->clock_rate);
     ms_filter_set_property(stream->decoder,MS_FILTER_PROPERTY_BITRATE,&pt->normal_bitrate);
    
-    ms_filter_set_property(stream->encoder,MS_FILTER_PROPERTY_FMTP, (void*)pt->fmtp);
-    ms_filter_set_property(stream->decoder,MS_FILTER_PROPERTY_FMTP,(void*)pt->fmtp);
+    ms_filter_set_property(stream->encoder,MS_FILTER_PROPERTY_FMTP, (void*)pt->send_fmtp);
+    ms_filter_set_property(stream->decoder,MS_FILTER_PROPERTY_FMTP,(void*)pt->send_fmtp);
     /* create the synchronisation source */
     stream->timer=ms_timer_new();
    
Index: talk/third_party/mediastreamer/msrtpsend.c
===================================================================
--- talk/third_party/mediastreamer/msrtpsend.c    (revision 7)
+++ talk/third_party/mediastreamer/msrtpsend.c    (working copy)
@@ -85,7 +85,7 @@
{
     guint32 clockts;
     /* use the sync system time to compute a timestamp */
-    PayloadType *pt=rtp_profile_get_payload(r->rtpsession->profile,r->rtpsession->payload_type);
+    PayloadType *pt=rtp_profile_get_payload(r->rtpsession->snd.profile,r->rtpsession->snd.telephone_events_pt);
     g_return_val_if_fail(pt!=NULL,0);
     clockts=(guint32)(((double)synctime * (double)pt->clock_rate)/1000.0);
     ms_trace("ms_rtp_send_process: sync->time=%i clock=%i",synctime,clockts);
Index: talk/base/base64.h
===================================================================
--- talk/base/base64.h    (revision 7)
+++ talk/base/base64.h    (working copy)
@@ -23,8 +23,8 @@
   static std::string decode(const std::string & data);
   static std::string encodeFromArray(const char * data, size_t len);
private:
-  static const std::string Base64::Base64Table;
-  static const std::string::size_type Base64::DecodeTable[];
+  static const std::string Base64Table;
+  static const std::string::size_type DecodeTable[];
};

} // namespace talk_base
Index: talk/base/stringutils.h
===================================================================
--- talk/base/stringutils.h    (revision 7)
+++ talk/base/stringutils.h    (working copy)
@@ -269,7 +269,7 @@
template<>
struct Traits<char> {
   typedef std::string string;
-  inline static const char* Traits<char>::empty_str() { return ""; }
+  inline static const char* empty_str() { return ""; }
};

///////////////////////////////////////////////////////////////////////////////

You killed all these errors? Congratulations! You can start talking with your gtalk friends with command ``call'' in talk/examples/call/ !

PS: If you are working with GCC 4.3.x, more strict checking is applied on the code. However, most errors can be fixed by adding some C headers into the #include fields, such as: <cstdlib>, <cstring>.

用 Linux 命令行工具自动追踪车票信息

前一篇博客中说到我买票失败的经历,也充分表达了我想买一张二手座票的意愿。怎么办呢?只好到网上各二手火车票信息平台去找了。心肠不好的人肯定幸灾了祸地在想:“哈哈,这个倒霉的小伙儿该对着浏览器不停地按 F5 了!” 你才 F5 呢,你们全家都 F5。那是典型的 Windows 用户的想法,不要以为 Linux User 跟你一样傻。

前面都是玩笑话 :),本文只是想介绍一下在 Linux 下有什么更方便的方法来追踪网页发布的信息,以展示 Linux 的命令行工具有多强大(也响应一下 Eric 师兄的文章:完全用键盘工作-3:常用的命令行工具)。

我们就拿火车网为例,通常情况下 Windows 用户为了在火车网上找一张二手火车票信息,会不断地到查询页面刷新,看有没有自己需要的车票。而一个 Linux 用户的做法会有何不同呢?一般来讲他会用工具来做这件事情,而不是在那傻刷,浪费时间。

怎么做呢?有很多种方法,我这里来介绍一种比较好玩的方法,用脚本自动跟踪信息,如果有结果就发送一个 Gtalk 消息给自己。

首先,写一个命令行发送 Gtalk 消息的 Python 脚本。其实我本打算用 freetalk 来做这件事的,奈何咱学识浅薄,不懂 freetalk 脚本该怎么写,也不知道 scheme 语言为何物。没办法,只好用 Python 来做了。下面内容就是用 Python 发送 gtalk 消息的脚本(需要 Linux 上装有 python-xmpp):

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# Usage: gtsent.py "SOMEBODY@gmail.com" "Message"

import xmpp
import sys

login = 'USERNAME' # without @gmail.com
pwd   = 'PASSWORD'

cnx = xmpp.Client('gmail.com', debug=[])
cnx.connect( server=('talk.google.com', 5223) )
cnx.auth(login, pwd, 'python')

cnx.send(xmpp.Message(sys.argv[1], sys.argv[2]))

将以上内容保存为 gtsend.py 文件,chmod u+x gtsend.py,修改 USERNAME 和 PASSWORD 为你的另一个非[常用] gmail 帐户名和密码。这样执行 ./gtsend.py SOMEBODY@gmail.com "Message" 就可以给 SOMEBODY@gmail.com 发送消息了(当然了,前提是 SOMEBODY@gmail.com 好友列表中有 USERNAME@gmail.com,请注意这里大写只是为了方便阅读)。

其次,写一个 Shell 脚本,用来追踪网页,过滤信息并发送 gtalk 消息。这个就更简单了,使用火车网提供的查询表单接口,用 wget 抓下来,再 grep 一下即可,bash 脚本如下:

#!/bin/bash
URL="http://www.huoche.com.cn/piao/2piaoserach.asp?ICheci=T65&type=0"
RESULT=`wget -O - $URL | iconv -f gbk -t utf8 | grep -i -e "t65.*南京.*硬座\
.*2008-9-28"`
if [ $(echo $RESULT | wc -c) -ge 5 ]
then
  /home/solrex/gtsend.py "YOURSELF@gmail.com" "$RESULT $URL"
fi

将该脚本保存为 get_tickt.sh,chmod u+x get_tickt.sh。这个脚本的工作流程是:wget 以 GET 方式提交对 T65 转让车票的查询,得到的结果输出到标准输出,然后将 GBK 编码转换为 UTF8 编码,再 grep 看是否含有“T65 南京 硬座 2008-9-28“关键词。如果有的话,用 gtsend.py 发送一个提醒消息给自己 gtalk 帐户;如果没有结果,什么都不做。

最后,将上面脚本加入 cron 列表每 10 分钟定时执行一次。 执行 crontab -e,添加下面一行即可(注意需要修改到该脚本的路径):

*/10 * * * * /home/solrex/get_ticket.sh

然后呢,你就可以高枕无忧,开着 Gtalk 等消息吧。当然,不一定能等得到 :(,唉,对我们来说, No news is BAD news!

当然,根据不同情况,你可以把追踪的信息换成别的东西。比如追女孩子的时候,可以用上面方法来实时跟踪她的最新博客,实时跟踪她在 BBS 上的留言,永远保持自己沙发的地位,说不定人家就感受到了你的关心,然后...具体方法我就不教了哈...

Google 词典和 Gtalk 翻译机器人

由于我工作的机器配置实在太低,1.8GHz CPU, 256M 内存,40G 硬盘,跑一个 Ubuntu 也是非常吃力,我平常只敢开三四个程序,这样每次切换程序还要等个十几秒,唉!

虽然我 Ubuntu 里也装了 Stardict 星际译王,但我轻易不敢再开一个程序,太慢了!现在我发现一个非常有意思的东西解决了我的困扰,Gtalk 翻译机器人。其实这是我在尝试另一项服务 Google 词典时无意中发现的,很奇怪的是,我记得曾经看到一个 Google 官方的关于 Gtalk 翻译机器人的页面,怎么再也找不到了?只搜索到一个 Gtalk 开发组的博客上的新闻链接

添加 Gtalk 翻译机器人很简单,就是选择添加好友,好友 email 为:语言缩写2语言缩写@bot.talk.google.com。比如汉英翻译的机器人名字是:zh2en@bot.talk.google.com,英汉翻译的机器人名字是:en2zh@bot.talk.google.com。当然了,还有更多,点上面的新闻链接可以查看。

用这个机器人有什么好处呢?一是方便,直接在聊天软件里就可以查词。就像我用那么落后的机器,打开一个词典软件能让它假死半分钟,而 IM 软件总是会开着的,打开一个聊天窗口显然方便和快很多;还有一个好处是 Google 将你的聊天内容记录到 Gmail 里,那么过一段时间整理一下聊天记录就是一个非常好的生词表 :)。查找聊天记录很简单,只需要在 Gmail 上方的搜索栏中输入:from: en2zh@bot.talk.google.com 再点搜索即可。

Goolge 词典也是非常好用的,但不知道为什么在 Google 首页上点 Language Tools 进去以后却没有词典的链接,只有到 more->even more 中找 Translate 才有。

为什么 Gtalk 不支持视频聊天?

这是一个网友问我的问题,当时我的回答是:大概 Jabber 协议不支持吧,这个问题你应该去问 Google。但是当我搜索了一下 Gtalk 的帮助和网络上的内容后,居然没有发现这个问题的解答(可能是我搜索技术不到家),这就变成了一个有趣的问题。

为什么 Gtalk 不支持视频聊天?在咨询了 Wikipedia 之后,我发现我的回答基本是对的,因为 Gtalk 使用的聊天协议不支持。

Gtalk 使用的通信协议叫做 Jabber,因特网工程工作小组(IETF)已经将 Jabber 的核心 XML 串流协定以 XMPP(Extensible Messaging and Presence Protocol) 之名,正式列为认可的即时通讯及 Presence 技术。而 XMPP 的技术规格已被出版为 RFC 3920RFC 3921。所以在一般的讨论中,我们不区分 Jabber 和 XMPP。

从上面的介绍中我们也可以看出,XMPP 的技术是基于可扩展标记语言(XML)的,所以呢它有它的很多优点,比如:分布式(想像一下用电子邮件聊天),可扩展(XML命名空间的特性),弹性佳(可用在不同领域,比如网络监控,游戏),多样性(Jabber协议的公开性使你不必被一款聊天软件绑架),安全(利用已有的SASL及TLS技术)。

但是也正因为 XML,XMPP 协议有一些缺点,正是这些缺点给 Gtalk 带来了限制。

缺点一:Scalability(规模可伸缩度?)--> Gtalk 不支持群组聊天(Group Chat)。

XMPP 的可伸缩度不好,当进行多用户聊天或者提供发布/订阅服务时,XMPP 会带来很多 overhead(网络开销),这样会大大降低整个系统的通信效率。一些组织正在通过添加协议扩展来解决这个问题,就比如 2007 年 6 月 Google 在 Gtalk Gadget(像我们通常在Gmail窗口中看到那个)中加入了 Group Chat 功能,但是直到现在桌面版 Gtalk 仍然不支持 Group Chat。

缺点二:No binary data(无法传输二进制数据)--> Gtalk 不支持视频聊天。

XMPP 协议的数据包通常是被编码成一个长 XML 文档,限制了它直接传输原始二进制数据流的能力,所以使用 XMMP 协议的聊天工具的文件传输一般使用其它协议,比如 HTTP 来实现,如果实在无法避免用 XMMP 协议传输文件等,它一般使用 Email 中常用的 base64编码。

但既然这样,为什么 Gtalk 支持语音聊天呢?这就要说到另一个协议,XMMP 的扩展协议 Jingle,这是一个还没有被 XMPP标准正式采纳的扩展,而 Gtalk 采用的 Jingle 协议和已提交的草案也有所不同。Jingle 协议使得 Gtalk 支持 p2p 的多媒体互动,比如语音聊天功能。

那既然有多媒体功能扩展,为什么 Gtalk 现在不支持视频聊天呢?我不相信 Google 的工程师没有尝试添加视频聊天功能,可能的原因我猜想是视频聊天的性能达不到。猜想的依据是什么呢?Gtalk API 文档,Google 说:In the future, we plan to support SIP signaling as well. 如果 Jingle 能完美支持视频聊天,大概没有需要去支持另外一个类似的多媒体协议吧。

所以呢,我们可以看到虽然 Gtalk 的当前版本有内存泄露 BUG,而一年多来(2007年1月5日发布Gtalk 1.0.0.104) Google 仍然没有发布新版本,这说明 Gtalk 项目组的精力被其它更重要的东西占用了,这个东西会不会是对 Gtalk 视频和群组聊天的支持呢?我们拭目以待。

喜欢玩新软件的朋友可以尝试一下这个:Google Talk Labs Edition,里面有一些好玩的新功能!

重要评论:作者可能对xmpp还不是很了解,文章有很多的臆想,有些不太对。xmpp协议是个总称,它包括核心协议,扩展协议等(xep)等,事实上核心协议只规定了很小很基本的一些功能,大量的功能都是在xep中规定的。而xep在逐步完善中,其中的很多功能处于实验阶段,google可能是嫌麻烦,每一次协议更新都要修改软件,索性等协议功能稳定了再去实现。xmpp协议里面是有群组聊天的,叫muc,但google只实现了它的一个子集,叫 groupchat,而没有完整实现muc。视频聊天和音频聊天本质上是一样的。google可能就是再等xep中关于jingle部分协议的稳定以后再实现。而你看到的这句话:“In the future, we plan to support SIP signaling as well.”在google talk刚推出的时候就已有了。而且那个时候它还说会推出各个操作系统平台的版本。而如今呢?所以事情的发展是会变化的。还有google那时候说要支持 sip,并不是xmpp功能有什么问题,而是想使im用户能够统一起来,互联互通。因为sip和xmpp都是被ietf接受的标准协议。

Google Talk 小徽章

无意间发现 Google 推出了一个新功能,Google Talk 小徽章,把一个 iframe 放到网站上,这样网站的访客就能直接通过点击这个小徽章里的链接匿名和发布者聊天,不需要 Google 帐户。

测试了一把,发现当状态标识为 "busy" 的时候,通过小徽章无法聊天;当状态标识为 "available" 时匿名访客才可以点击它来进行聊天。由于是匿名,聊天时发布者方显示的对方名字是 "Guest"。

想看看效果?看我的个人主页,或者博客右侧 widgets。