Thursday, November 4, 2010

使用MinGW编译FFmpeg

友情提示: 编译FFmpeg本来就是一件体力活, 使用MinGW来编更是相当蛋疼, 因此, 如果你没有刚刚作出一个非常艰难的决定, 请立即停止这种自虐行为.

FFmpeg的人估计恨死Window$了, 如果你曾经有过使用VS编译的想法, 现在可以放心抛弃这种打算了. 这时如果你依然很不幸需要用到MinGW来编译, 那么下面的一些提示应该对你的编译过程有所帮助.

  1. configure的参数会稍微特别点:
    $ ./configure --enable-memalign-hack --disable-debug
    
    --disable-debug建议加上, 否则编译出的archive会很大很大...
  2. 现在先不忙着敲make, 我们需要patch一下MinGW的库文件, 否则就会出现诸如"implicit declaration of function 'strncasecmp'"这样的错误. 打开这个链接: http://fate.arrozcru.org/mingw32/patches/, 依照着里面的diff文件挨个patch吧, 完成就可以顺利编译了, 别忘了还要安装Yasm哦.

  3. 编译完成之后你应该可以在各个子目录中找到archive文件, 拿着这些文件做爱做的事吧.

不过小a娘也不好调教, 如果你需要再拿去链接, 可以参考FFmpeg这篇官方指南: http://www.ffmpeg.org/general.html#SEC23. 你可能还会遇到下面一些链接错误.

  • "unresolved external symbol _strcasecmp", 这个不知是不是MinGW的bug, 居然没有提供strcasecmp()函数, 解决方法是自己实现一个相同的函数, 可以参考GNU C Library
  • "unresolved external symbol __imp____lc_codepage", 这是一个更加诡异的错误, 解决方法我在这篇文章中找到, 主要就是把"Runtime Library"从/MD改为/MT
  • "error LNK2005: XXX already defined in LIBCMT.lib MSVCRT.lib", 最诡异错误出现, 操蛋的VS库啊, 在这篇文章的帮助下, 找到了解决办法, 在"Ignore Specific Library"里添加"MSVCRT.lib"即可.

Friday, October 15, 2010

Wish

April Story

淡淡的灯光, 轻轻的音乐, 飘扬的樱花, 已经许久没有看岩井俊二的电影, 对于《四月物语》, 一看就能知道这是他的电影. 电影结束得让我有点意犹未尽, 榆野卯月一个人打着红色的雨伞站在那里, 默念着心中的话语. 看着雨伞上滴落的雨水, 绽放出一丝笑容, 曾几何时, 我也会这样傻傻地干着傻傻的事. 不是因为雨水多么可爱, 只因心中有那么一股暖流在那时流过, 因为某个人, 因为某件事. 在那个阳光明媚的午后, 我也傻傻地望着不远的转角, 期待着某个身影的出现.

喜欢电影从头至尾的清新感觉, 日本干净的街道, 温馨的房间, 以及空旷草地上那抹夕阳. 我跟珍妮说我以后也想要一个很温馨, 很舒适的房子, 不需要多大, 我也不在乎它是租来的, 还是买来的. 但是它会有那种米色的窗帘在卧室, 当窗外阳光透进来时屋内会格外温馨. 有一张很大很舒服的床, 让人看到的第一眼就想要跳上去. 客厅会有布料沙发, 上面或许还有几个柔软的抱枕和玩具. 窗帘是白色的, 会给人一种清新的感觉. 还要一盏小台灯, 夜幕降临时它能为我照耀出轻柔的灯光. 也许, 也许我还能抱着你静静地倚在那里.

中午和同事在附近尝了广东的早茶, 餐厅是一个有着幽暗灯光的地方, 倒很适合情侣. 虽然叫做早茶, 不过不是去品茶, 广东的早茶有着各种小吃和点心, 再配上几碗恬淡的粥, 油而不腻. 也许我也该慢慢学着懂得生活, 从小到大的教育是教我怎样学习, 怎样考上好学校, 却没有教我怎样生活, 生活的本质又是什么. 当集体的信仰缺失, 就会造成一个民族的精神滞后.

我跟大猫说我们还年轻, 有那么多的事等着我们去做, 不管我们的未来如何, 总要知道希望就在前方, 就像很久之前一位同学告诉我说: 希望一定会实现的. 这句话我一直记着.

Sunday, September 19, 2010

那些与彩蛋有关的事情

原文发表于「桃源」: http://linux.cuit.edu.cn/?p=1006

前几天@liancheng同学在Twitter上传说中GCC有一个关于#pragma的彩蛋, 大致是说Stallman同学对#pragma很有意见, 因此在GCC中埋了一个彩蛋, 只要发现#pragma就会蹦出俄罗斯方块. 这个很有意思的八卦让我想要一探究竟, 经过一番查找, 这个彩蛋的确是存在的, 不过不是蹦出俄罗斯方块, 根据Wikipedia上一篇文章的介绍, GCC会试图依次启动Hack, Rogue, 以及Emacs中的Towers of Hanoi (汉诺塔), Hack和Rogue都曾经在「桃源」的另一篇文章[经典游戏推荐: NetHack]中提及. 不过这个彩蛋现在已经不存在了, Stallman同学曾经在接受采访时谈到 (请在采访稿中搜索"Easter eggs"), 这个彩蛋其实是针对C标准开的一个玩笑, 后来由于某种原因被移除. 幸运的是, GCC的古老代码依然存在, 下面是从GCC 1.21中提取出来的"遗体":
/* This was a fun hack, but #pragma seems to start to be useful.
   By failing to recognize it, we pass it through unchanged to cc1.  */

/*
 * the behavior of the #pragma directive is implementation defined.
 * this implementation defines it as follows.
 */
do_pragma ()
{
  close (0);
  if (open ("/dev/tty", O_RDONLY) != 0)
    goto nope;
  close (1);
  if (open ("/dev/tty", O_WRONLY) != 1)
    goto nope;
  execl ("/usr/games/hack", "#pragma", 0);
  execl ("/usr/games/rogue", "#pragma", 0);
  execl ("/usr/new/emacs", "-f", "hanoi", "9", "-kill", 0);
  execl ("/usr/local/emacs", "-f", "hanoi", "9", "-kill", 0);
nope:
  fatal ("You are in a maze of twisty compiler features, all different");
}

关于#pragma彩蛋的故事结束了, 但彩蛋本身就像一个充满魔力的物体, 一直是Geek间津津乐道的话题. Linux中的彩蛋不胜枚举, LinuxTOY有一篇文章大概介绍了其中的一小部分. 还有著名的Google Reader中的Konami Code彩蛋. xkcd的命令行版unixkcd更是隐藏了大量彩蛋, 其中就包括Konami Code. 还有一个颇有意思的彩蛋, 在Emacs中按下"M-x butterfly C-M-c"即可, 这个彩蛋的由来是因为xkcd上曾经有一篇漫画 (这篇漫画很有趣, 推荐大家看看, Linux程序员定会泪牛满面, lol) 虚构了Emacs中有这么一个彩蛋, 于是开发Emacs的那群家伙就真的在Emacs 23.1中加入了这个彩蛋...

彩蛋是程序员创造的独一无二的标志, 也许是为了好玩, 也许是为了吐槽, 也许是为了方便调试, 总之彩蛋给这个世界带来了一个值得纪念的回忆. 如果你还知道什么有趣或者有意义的彩蛋, 欢迎分享, :)

For LaN

Thursday, August 19, 2010

小工具介绍: XBindKeys, Global Menu

原文发表于「桃源」: http://linux.cuit.edu.cn/?p=987

小高子一直都是一个近乎狂热的键盘控, 寻找各种可以通过键盘实现的操作. 自从结识了GNOME Do这个神器一般的软件, 启动各种程序再也没有用到鼠标. 不过有一点是GNOME Do办不到的, 当Firefox有多个账户时, 这时通过GNOME Do启动Firefox就有点混乱, 每次必须通过冗长的命令行在多个Firefox账户间切换. 可不可以把这些命令绑定到键盘上呢? 那就是今天的第一位主角了: XBindKeys.

安装好XBindKeys之后, 先生成默认的配置文件:
$ xbindkeys --defaults > ~/.xbindkeysrc
配置文件的语法规则也很简单, 第一行为命令, 第二行为快捷键, 比如:
"firefox -P default -no-remote"
  control + shift + f
这样就可以把任何命令绑定到键盘上了.

Global Menu最初是大猫同学在Twitter上推荐的, 听名字很容易知道这个软件是用来干嘛的, 效果图如下:

Global Menu

怎么安装呢? 这里有一个官方安装指引, Ubuntu用户可以通过PPA源:
$ sudo add-apt-repository ppa:globalmenu-team/ppa
$ sudo apt-get update
$ sudo apt-get install gnome-globalmenu
将menu bar放在屏幕最顶端的设计源于Mac OS, 不过这可不仅仅是单纯的创新或者为了好看, vgod同学的这篇文章详细分析了这种GUI设计背后的数学背景.

Sunday, June 20, 2010

Linux下各种Buffer的比较

原文发表于「桃源」: http://linux.cuit.edu.cn/?p=919

类型 默认大小[1] 存储位置 操作函数 备注
标准I/O流[2] File BUFSIZ[3] (8192) 或者 st_blksize[4] (4096) User Space stat(2), setvbuf(), fflush() 每一个标准I/O流都有一个buffer
stdin stdin->_IO_buf_end[5] - stdin->_IO_buf_base (1024)
stdout stdout->_IO_buf_end - stdout->_IO_buf_base (1024)
stderr 1
TCP Receive Buffer SO_RCVBUF[6] (87380) Kernel Space getsockopt(), setsockopt() 每一个socket都有两个buffer
Send Buffer SO_SNDBUF (16384)
UDP[7] Receive Buffer SO_RCVBUF (114688)
Send Buffer SO_SNDBUF (114688)

脚注:

  1. 括号中的数字为我电脑上的实际大小, 单位为字节. 测试环境: Ubuntu 9.10, 内核版本2.6.31, GNU C library版本2.10.1, 文件系统ext4.
  2. 标准I/O流buffer的默认大小是由具体的C函数库实现决定的, 比如GNU C library就使用st_blksize作为默认大小. 每个流的buffer是在创建好流之后, 第一次调用标准I/O库函数对流进行操作时通过malloc()函数分配的. (参见《APUE》5.4节与5.12节)
  3. BUFSIZ是定义在<stdio.h>头文件中的宏.
  4. st_blksize是"struct stat"中的成员, 通过stat(2)函数获得.
  5. _IO_buf_end以及_IO_buf_base是"struct _IO_FILE"中的成员, "struct _IO_FILE"的定义通常在<libio.h>中. 其实在<stdio.h>中可以看到"typedef struct _IO_FILE FILE;", 也就是我们经常使用的FILE指针指向的结构体了.
  6. SO_RCVBUF是socket的选项名, 可以通过getsockopt()函数获得大小, 以及setsockopt()设置大小. 后同. (参见《UNP》2.11节与7.5节)
  7. UDP类型的socket实际上是不存在buffer的, 这里的大小只是用来约束数据报的最大长度. (参见《UNP》2.11节)

参考资料:

  1. W. Richard Stevens and Stephen A. Rago. Advanced Programming in the UNIX Environment, 2/e. Addison-Wesley Professional, June 17, 2005, ISBN 0201433079.
  2. W. Richard Stevens, Bill Fenner, and Andrew M. Rudoff. UNIX Network Programming, Volume 1: The Sockets Networking API, 3/e. Addison-Wesley Professional, November 21, 2003, ISBN 0131411551.
  3. Helali Bhuiyan, Mark McGinley, Tao Li and Malathi Veeraraghavan. TCP Implementation in Linux: A Brief Tutorial. Available online from http://www.ece.virginia.edu/mv/research/DOE09/publications/TCPlinux.pdf

P.S. 如有不当之处, 还望指教.

Monday, May 24, 2010

最后的战役

John Basilone

献给John Basilone & Lena Mae Riggi

《The Pacific》第8集是属于John Basilone的, 前几集的大肆渲染让我感到很是困惑, 如此一个言过其实的角色凭什么占有这么多的戏份. 直到第8集, 我看到了他身上的正气, 看到了他心中不甘沉沦的理想. 当周围的人们因为他的英勇事迹欢呼雀跃, 因为他的荣耀与光环相敬如宾, Basilone想的不是怎样利用自己的影响力去多捞点好处, 不是每天在办公室喝喝咖啡, 玩玩打字机. 可能你也猜到了, Basilone是一个有抱负的人. 作为二战中唯一一个同时获得荣誉勋章与海军十字勋章的海军陆战队队员, 他选择了放弃舒适的生活, 回到那个能够让他的血液沸腾的地方. 他属于战场.

在新兵训练营遇见Lena是Basilone生命的转折, 前面几集大家可能都会把他当作是一个花花公子, 因为每次在他身边出现的女人都不一样. 在酒店就餐那段Lena演得真好, 那语气, 那神情, "Ooh. Ah. There goes John Basilone. The hero of Guadalcanal." Basilone真是尴尬得无地自容, 他需要向Lena证明自己的心是可以依靠的. 让我们跳过谈情说爱的剧情, 反正最后帅哥肯定是赢得了美女的芳心. 在Basilone即将出征奔赴战场之前, Lena嫁给了他. 在我看来, 这是全集最温情的时刻, Basilone躺在床上, 深情地望着Lena美丽的背影, Lena笑笑, 问到"What?", Basilone什么也没有说, 眼神中已经掺杂了些许泪光, 把目光转向了窗外, 那是一片平静的大海. 奔赴战场, 命运未卜, 怎能忍心留下Lena独自一人. Lena说她可以等, 而我只希望导演能够让Basilone平安回家.

1945年2月19日, 硫磺岛登陆战. 在长达10分钟的镜头里, 导演几乎把每一帧都给了Basilone, 这也许就意味着他最终的命运. 即使是在一年后重返战场, Basilone依旧骁勇善战. 在他倒下的那一刻, 世界静止了, 时间也静止了. Basilone微睁的双眼望着远方, 他心中所想, 只有Lena.

Tuesday, May 11, 2010

split()函数的C、C++标准库实现

2010.5.11 22:52更新: 在大猫同学的强烈要求下, 增加了测试代码中动态分配空间的销毁.

2010.5.14 22:46再更新: 上次更新代码之后, 大猫同学指出还是存在内存泄漏问题, 各位同学一定要注意malloc()free()必须成对使用哦~

最近有这么一个需求, split()函数大家都用过, 将字符串按特定的分隔符 (delimiter) 分割成几个部分, 这在脚本语言中早已司空见惯. 但C/C++中是没有的, 于是就给了我这样闲得蛋疼的人消磨时间的机会, 下面分别是用C和C++的标准库实现的split()函数.

首先是C语言的实现, 这个是我自己写的, 函数返回一个指向char*数组 (也就是字符串数组) 的指针.
#include <string.h>
#include <stdlib.h>

/*
 * split - Break a string @s into an array of strings.
 * @s: The string to be split.
 * @delim: The delimiter.
 * @n: The length of the array of strings, created by splitting @s.
 */
char** split(char* s, char delim, int* n)
{
    char* p;
    char* q;
    *n = 1;
    p = q = s;
    /*
     * Calculate the value of @n.
     */
    while ((p = strchr(q, delim)) != NULL)
    {
        ++*n;
        q = p + 1;  // skip the delimiter
    }
    p = q = s;

    char** sep = (char**) malloc(sizeof(char*) * (*n));  // the array of strings
    int i;
    for (i = 0; i != *n; ++i, q = p + 1)
    {
        /*
         * When there isn't delimiter, put `p' point to the end of @s.
         * Same like behavior of strchrnul() library function.
         */
        if ((p = strchr(q, delim)) == NULL)
            p = s + strlen(s);

        int len = p - q;
        sep[i] = (char*) malloc(sizeof(char) * (len + 1));
        strncpy(sep[i], q, len);
        sep[i][len] = '\0';
    }

    return sep;
}
可以使用下面的代码进行测试:
#include <stdio.h>
#include <stdlib.h>

char** split(char*, char, int*);

int main(int argc, char* argv[])
{
    char str[] = ":GFW::is:evil:";
    int i, n;
    char** sep = split(str, ':', &n);
    for (i = 0; i != n; ++i)
        printf("%s\n", sep[i]);

    for (i = 0; i != n; ++i)
    {
        free(sep[i]);
        sep[i] = NULL;
    }
    free(sep);
    sep = NULL;

    return 0;
}

C++实现来自Stack Overflow的一篇讨论, 我稍微进行了一些修改.
#include <sstream>
#include <vector>

using namespace std;

vector<string> split(const string s, char delim = ' ')
{
    vector<string> elems;
    istringstream ss(s);
    string item;
    while (getline(ss, item, delim))
    {
        elems.push_back(item);
    }
    if (item == "")
        elems.push_back(item);
    return elems;
}
代码很简洁, 测试代码就不写了, 大家应该都能看懂. 其实大名鼎鼎的Boost也实现了split(), 但毕竟不是任何时候都能使用Boost.

Sunday, May 9, 2010

转换多个JPG文件到PDF文件

原文发表于「桃源」: http://linux.cuit.edu.cn/?p=908

最近需要将大量JPG格式的图片全部转换到一个PDF文件里, Google之后发现Window$下清一色的收费软件, 即使有试用版, 也会带有很多限制. 而Linux下则从来都不缺乏这种实用的小工具, 在bitPrison.net的一篇文章中介绍了通过ImageMagick中的convert组件进行转换的方法, 经过尝试, 发现这种方法效率很低, 并且会占用很高的CPU. 该篇文章后面的评论提供了一种更优的方法, 使用PDFjam包中的pdfjoin命令完成, 具体步骤如下:
$ rename 's/\.jpg$/\.pdf/' *.jpg
$ pdfjoin --outfile alist.pdf *.pdf
这里先将所有JPG文件重命名为PDF文件, 接着便可使用pdfjoin命令进行合并.

其实GNOME或者KDE下的程序都可以直接将图片打印为PDF文件, 比如使用Eye of GNOME打开图片之后, 选择打印就会出现“Print to File”选项, 这种方法适用于少量的或者临时性的转换需求.

Monday, April 26, 2010

小技巧: 在C++中实现在main函数之前及之后执行代码

对于C++程序而言, 程序执行的入口总是main函数. 怎样才能在执行main函数之前执行一段代码呢? 答案是全局对象 (global object). 程序在执行时因为会先初始化全局变量, 当这个全局变量是一个对象时, 自然会调用相应的构造函数, 这时构造函数中的代码就得以执行.

那怎样在main函数退出之后再执行一段代码? 答案依然是全局对象. 当程序退出时, 全局变量必须销毁, 自然会调用全局对象的析构函数, 剩下的就同构造函数一样了.

下面是一段测试代码:
#include <iostream>

using namespace std;

class A
{
public:
    A()
    {
        cout << "1" << endl;
    }

    ~A()
    {
        cout << "4" << endl;
    }
};

A a;

int main(int argc, char* argv[])
{
    cout << "2" << endl;

    cout << "3" << endl;

    return 0;
}

P.S. 本来想通过汇编进一步分析的, 无奈功力还不够深厚, 希望以后能够搞懂. 若有高人了解, 还望指点.

Tuesday, March 30, 2010

OAuth协议及开发简介

这是昨天三叶草小组内部交流我的主题, 因为考虑到多数人对Twitter不熟悉, 所以用人人网做的例子, 但其实人人网是不支持OAuth的.