2013年8月31日

Python 2.7 + Windows XP/7 命令行下中文,Unicode 输入输出,文件名输入输出

在 Win32 下,各种 codepage 的瞎搞实在是混杖。 GBK, mbcs,utf-16le , cp65001 各种encoding + charset ,再加上 Python2 系列的走非 wchar 系的 Win API,各种乱码,让 cmd.exe 命令行下的 python2 基本上成了国际废物。

特别是理论上,WindowsXP 以后文件名应该是以 UFT-16LE 存在硬盘上的,可以覆盖所有Unicode 分区。可是 Python 2.7 里,通过命令行得到的文件名,只是当前 Codepage 编码的字符串。我们的目的是得到 Unicode 编码覆盖的所有码区的文件名。

输出也是这个问题,CP936 下 print()只能输出GBK码,不能输出所有 Unicode 范围的文本。

WinXP 控制台如此,Win7 也未必能好,倒是 Python3 应该是问题少些吧。

百度了一圈儿 Python2.7 怎么接受 console 下的繁简体,中日韩,英法俄语言共用,结果是无解。只好再搜英文网页,结果发现还真有料。记录在此,以供传播。

基本思想就是通过 ctypes 调用 windll 相关的宽字符 API,这样就可以直接得到或者输出 unicode 的结果了。

从命令行接受 unicode 编码的选项相对容易一些,使用 shell32.GetCommandLineArgvW 就差不多了,输出反而要复杂些,因为要替代 output stream,结果得弄一个全套的 io class 。

命令行读入 Unicode 选项: http://stackoverflow.com/a/846931

def win32_unicode_argv():
    """Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
    strings.

    Versions 2.x of Python don't support Unicode in sys.argv on
    Windows, with the underlying Windows API instead replacing multi-byte
    characters with '?'.
    """

    from ctypes import POINTER, byref, cdll, c_int, windll
    from ctypes.wintypes import LPCWSTR, LPWSTR

    GetCommandLineW = cdll.kernel32.GetCommandLineW
    GetCommandLineW.argtypes = []
    GetCommandLineW.restype = LPCWSTR

    CommandLineToArgvW = windll.shell32.CommandLineToArgvW
    CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
    CommandLineToArgvW.restype = POINTER(LPWSTR)

    cmd = GetCommandLineW()
    argc = c_int(0)
    argv = CommandLineToArgvW(cmd, byref(argc))
    if argc.value > 0 and not hasattr(sys, "frozen"):
        # Remove Python executable and commands if present and not frozen
        start = argc.value - len(sys.argv)
        return [argv[i] for i in xrange(start, argc.value)]
命令行Unicode输出: http://stackoverflow.com/a/3259271

def win32_unicode_output():
    import codecs
    from ctypes import WINFUNCTYPE, windll, POINTER, byref
    from ctypes.wintypes import BOOL, HANDLE, DWORD, LPWSTR, LPVOID

    original_stderr = sys.stderr

    # If any exception occurs in this code, we'll probably try to print it on
    # stderr, which makes for frustrating debugging if stderr is directed to
    # our wrapper.  So be paranoid about catching errors and reporting them to
    # original_stderr, so that we can at least see them.
    def _complain(message):
        print(message if isinstance(message, str) else repr(message),  file=original_stderr)

    # Work around <http://bugs.python.org/issue6058>.
    codecs.register(lambda name: codecs.lookup('utf-8') if name == 'cp65001' else None)

    # Make Unicode console output work independently of the current code page.
    # This also fixes <http://bugs.python.org/issue1602>.  Credit to Michael
    # Kaplan <http://blogs.msdn.com/b/michkap/archive/2010/04/07/9989346.aspx>
    # and TZOmegaTZIOY
    # <http://stackoverflow.com/questions/878972/windows-cmd-encoding-change-causes-python-crash/1432462#1432462>.
    try:
        # <http://msdn.microsoft.com/en-us/library/ms683231(VS.85).aspx>
        # HANDLE WINAPI GetStdHandle(DWORD nStdHandle);
        # returns INVALID_HANDLE_VALUE, NULL, or a valid handle
        #
        # <http://msdn.microsoft.com/en-us/library/aa364960(VS.85).aspx>
        # DWORD WINAPI GetFileType(DWORD hFile);
        #
        # <http://msdn.microsoft.com/en-us/library/ms683167(VS.85).aspx>
        # BOOL WINAPI GetConsoleMode(HANDLE hConsole, LPDWORD lpMode);

        GetStdHandle = WINFUNCTYPE(HANDLE, DWORD)(("GetStdHandle", windll.kernel32))
        STD_OUTPUT_HANDLE = DWORD(-11)
        STD_ERROR_HANDLE = DWORD(-12)
        GetFileType = WINFUNCTYPE(DWORD, DWORD)(("GetFileType", windll.kernel32))
        FILE_TYPE_CHAR = 0x0002
        FILE_TYPE_REMOTE = 0x8000
        GetConsoleMode = WINFUNCTYPE(BOOL, HANDLE, POINTER(DWORD))(("GetConsoleMode", windll.kernel32))
        INVALID_HANDLE_VALUE = DWORD(-1).value

        def not_a_console(handle):
            if handle == INVALID_HANDLE_VALUE or handle is None:
                return True
            return ((GetFileType(handle) & ~FILE_TYPE_REMOTE) != FILE_TYPE_CHAR
                    or GetConsoleMode(handle, byref(DWORD())) == 0)

        old_stdout_fileno = None
        old_stderr_fileno = None
        if hasattr(sys.stdout, 'fileno'):
            old_stdout_fileno = sys.stdout.fileno()
        if hasattr(sys.stderr, 'fileno'):
            old_stderr_fileno = sys.stderr.fileno()

        STDOUT_FILENO = 1
        STDERR_FILENO = 2
        real_stdout = (old_stdout_fileno == STDOUT_FILENO)
        real_stderr = (old_stderr_fileno == STDERR_FILENO)

        if real_stdout:
            hStdout = GetStdHandle(STD_OUTPUT_HANDLE)
            if not_a_console(hStdout):
                real_stdout = False

        if real_stderr:
            hStderr = GetStdHandle(STD_ERROR_HANDLE)
            if not_a_console(hStderr):
                real_stderr = False

        if real_stdout or real_stderr:
            # BOOL WINAPI WriteConsoleW(HANDLE hOutput, LPWSTR lpBuffer, DWORD nChars,
            #                           LPDWORD lpCharsWritten, LPVOID lpReserved);

            WriteConsoleW = WINFUNCTYPE(BOOL, HANDLE, LPWSTR, DWORD, POINTER(DWORD), LPVOID)(("WriteConsoleW", windll.kernel32))

            class UnicodeOutput:
                def __init__(self, hConsole, stream, fileno, name):
                    self._hConsole = hConsole
                    self._stream = stream
                    self._fileno = fileno
                    self.closed = False
                    self.softspace = False
                    self.mode = 'w'
                    self.encoding = 'utf-8'
                    self.name = name
                    self.flush()

                def isatty(self):
                    return False

                def close(self):
                    # don't really close the handle, that would only cause problems
                    self.closed = True

                def fileno(self):
                    return self._fileno

                def flush(self):
                    if self._hConsole is None:
                        try:
                            self._stream.flush()
                        except Exception as e:
                            _complain("%s.flush: %r from %r" % (self.name, e, self._stream))
                            raise

                def write(self, text):
                    try:
                        if self._hConsole is None:
                            if isinstance(text, unicode):
                                text = text.encode('utf-8')
                            self._stream.write(text)
                        else:
                            if not isinstance(text, unicode):
                                text = str(text).decode('utf-8')
                            remaining = len(text)
                            while remaining:
                                n = DWORD(0)
                                # There is a shorter-than-documented limitation on the
                                # length of the string passed to WriteConsoleW (see
                                # <http://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232>.
                                retval = WriteConsoleW(self._hConsole, text, min(remaining, 10000), byref(n), None)
                                if retval == 0 or n.value == 0:
                                    raise IOError("WriteConsoleW returned %r, n.value = %r" % (retval, n.value))
                                remaining -= n.value
                                if not remaining:
                                    break
                                text = text[n.value:]
                    except Exception as e:
                        _complain("%s.write: %r" % (self.name, e))
                        raise

                def writelines(self, lines):
                    try:
                        for line in lines:
                            self.write(line)
                    except Exception as e:
                        _complain("%s.writelines: %r" % (self.name, e))
                        raise

            if real_stdout:
                sys.stdout = UnicodeOutput(hStdout, None, STDOUT_FILENO, '<Unicode console stdout>')
            else:
                sys.stdout = UnicodeOutput(None, sys.stdout, old_stdout_fileno, '<Unicode redirected stdout>')

            if real_stderr:
                sys.stderr = UnicodeOutput(hStderr, None, STDERR_FILENO, '<Unicode console stderr>')
            else:
                sys.stderr = UnicodeOutput(None, sys.stderr, old_stderr_fileno, '<Unicode redirected stderr>')
    except Exception as e:
        _complain("exception %r while fixing up sys.stdout and sys.stderr" % (e,))

当然最好还是去源网页参考原来的内容,以免这里有引用错误。

2010年10月20日

Assembly Language book for beginner
汇编语言入门书

推荐一本汇编语言入门的书:PC汇编语言 (PC Assembly Tutorial) ,推荐的原因不是说它写得多好,而是它写的相当简洁。

中文翻译版 177 页,每页字不多。很适合没有耐心的C/C++程序员使用。花三天时间,每天几个小时就能轻松掌握了。看完就能用汇编加速C/C++程序,这三天太值了。

而且内容叙述和例子的比例也很恰当,一边看,一边把例子敲一遍出来,脑力体力结合,人性化得很。

中文版的翻译不多的几个细节表述的不清楚,要多想两遍,不过总体来说,非常清晰,容易理解。感谢原作者和翻译者。

对于书里描述不清楚的地方,再配合著名的 汇编的艺术(The Art of Assembly Language Programming) 查找相应的指令解释来看,就补齐了。

语言掌握后,更多的指令用法,就像查程序库API的用法一样,用到的时候翻汇编的艺术就可以了。

汇编的艺术的问题在于太全了,丛头到尾,那么厚一本书,怎么看都看不下去。做为参考书,具体编程的时候查看,再深入研究还差不多,用来入门不合适。



I've spent 3 days to finish the Chinese version of PC Assembly Tutorial. After reading the book, one should be able to get start with accelerating C/C++ programs use assembly programming. It's very easy to follow and has the right amount of examples to get you feel the language.

Highly recommend to C/C++ programmers who want to get start with the assembly language.

The reason I recommend the book is that the book is very concise and easy to follow. There's only 177 pages with not too many words per page for the Chinese version. Of course any other language version will be a little longer since Chinese script is one of the most concise script in the world. But the point still stands.

The book comes with translated version to several languages.

On the other hand, the Art of Assembly Language Programming is too comprehensive for beginners. I'd guess it's a good reference book for assembly programming but definitely not a good dive in book. At least I was not patient enough to finished it on several tries.

2010年5月2日

mp3 tag encoding/charset convert

So I was trying to convert id3 tags of some music files from Chinese GBK encoding to UTF-8 such that the Audacious can read the tags. Well, after thoroughly searching the web, I finally found one that is packaged on Debian/Linux.

The program is called mid3conv. It's kinda of a demo script for the python-mutagen package, hence the 'm' before the id3conv name.

Apparently, the demo tools in the mutagen is not well advised. Those are:

  • mutagen-inspect (1) - view Mutagen-supported audio tags
  • mutagen-pony (1) - scan a collection of MP3 files
  • mid3iconv (1) - convert ID3 tag encodings
  • mid3v2 (1) - audio tag editor similar to 'id3v2'
  • moggsplit (1) - split Ogg logical streams

So if you want to convert some ID3 tags in legacy encoding or charset of CJK (Chinese/Japanese/Korean) in your mp3 files, try to install 'python-mutage' and use the command line tool mid3conv.

2010年4月8日

Solve error: the symbol 'grub_puts_' not found

Everybody and their mom know that Grub2 (version 1.98 as of today) sucks. I got bitten by it twice in 2 grub upgrades on my Debian box.

The latest one is the error: the symbol 'grub_puts_' not found.
And a useless prompt: grub rescue>

After fooling around with all the tinycore linux, kubuntu livecd images, I found the easy way out.

I just burned a Super Grub Disk and boot into my Debian with it using an old /boot/grub/menu.lst leftover from the grub 0.97 era.

I have to use the 0.9799 version of the super grub disk since the latest grub2 compatible versions 1.30/1.21 images are not bootable at all.

Once inside the Debian, do a
    # grub-install /dev/sda

to put the grub back on my 1st hard drive. voila!


Yes, Debian forces this 1.98 version of grub2 onto users. Fuck whoever made that decision.

2009年1月13日

Using firebug console on chrome extensions

When working on a firefox extension, you cannot use firebug console.log directly from your XUL chrome/overlay.js because the console belongs to the document window but in XUL, you get a XUL window.

The equivalent of a window in chrome overlay is the "content" but it is XPCNativeWrapper wrapped which hide all the javascript methods of an Object. We need to unwrap the content first before we can get a console. So to get the console instance, you need this:


DEBUG=false;
// Wrap firebug console.log
function log() {
if (!DEBUG) return;
var console = content.wrappedJSObject.console;
if (console.firebug) {
console.log.apply(console, arguments);
}
}

Safely_accessing_content_DOM_from_chrome

2008年6月9日

Load arbitary module to any gtk+ applications
为任意 gtk+ 程序加载模块

gtk+ 图形库有一个特殊的环境变量,可以让你为任意的 gtk+ 程序加载模块,而不需要这个程序本身支持插件,或者扩展功能。只要在运行程序前,设定 GTK_MODULES 这个环境变量,让它指向所有需要加载的模块。而GTK_MODULES指定的模块,本身是符合 gtk 模块接口 的动态模块。

You can load arbitrary gtk modules/plugins to any gtk+ application without the application explicitly support gtk modules. All you need to do is set the environment variable GTK_MODULES to all the modules or plugins you want to load before starting the application. The modules are dynamic libraries compliant gtk module API.

2006年10月7日

Mplayer Soft Volume
Mplayer 的软件音量控制

最近才意识到 mplayer 是有软音量控制的,就是说它的音量改变或者静音可以不影响其他软件的音量。有时候边听音乐边用 mplayer 的时候会希望暂时关掉它的音量,以前它会直接把音卡的音量关了,音乐也别听了。现在意识到可以在启动的时候加 -softvol 选项来使用软音量控制。这样静音的就只是 mplayer 本身,而不会影响到我的 QuodLibet 了。现在我把这个选项直接写到 ~/.mplayer/config 里了。

Just realized that you could have soft volume control when using mplayer. It seems that the function was already there since 2004. Well, I only found it just now. Now I can mute mplayer while listen to music with my QuodLibet. The option is -softvol for your curiosity.

2006年4月16日

转载一个UPnP流程的分析文章

看到一个关于UPnP中控制点操作流程的东西,觉得说的挺清楚。对UPnP概念的初步理解很有帮助,比网上大量的UPnP协定,描述要容易上手的多了。挺旧的一篇文章,2004年的。


当然这是针对NAT穿透这种简单应用的流程描述,但是上手自然是越简单越好了。何况现在UPnP的应用大抵还是NAT穿透。



UPnP中Control Point的基本流程

2006年4月7日

Istanbul: Record desktop on Linux
伊斯坦布尔:Linux下桌面录像

Istanbul
This is a gstreamer application writen in Python. It can be used to record desktop activities into a movie (Ogg Theora).




伊斯坦布尔是一款在Linux下记录桌面活动的程序。是用 Python + gstreamer 写的。结果存放在 Ogg Theora 格式的电影下。


http://live.gnome.org/Istanbul

2006年3月3日

Put Chinese fonts in front of Japanese fonts in fontconfig
中文字型优先,日文字型靠后

On Debian systems, when both Chinese and Japanese fonts are installed, the Japanese fonts will have the priority being used in front of the Chinese ones. The result is that the Serif and Sans-Serif fonts will be totally messed with ugly Japanese glyphs under a Chinese locale. You would think that fontconfig can select the right fonts accroding the current locale. :(



The solution? Using a ~/.fonts.conf file to set Chinese font as the preferred one for Serif and Sans-Serif. Direct editing /etc/fonts/fonts.conf will has your settings reset on upgrade. For a system-wised customization, do it in /etc/fonts/local.conf, this is on a Debian box.



Another advantage on using ~/.fonts.conf is that you might have users using different locales (hint: Japanese) on the same box. You don't want to interfere with other people's fonts.



修改 ~/.fonts.conf 文件,提高中文字型在系统字型中的优先次序。
简单来说,/etc/fonts/fonts.conf 会先加载我们的 ~/.fonts.conf ,然后才设定系统字型(Serif, Sans-Serif)的组合字型的优先次序。我们先设定了,系统的就在我们屁股后面了。



Use the following command to find out fonts that fontconfig knows:

使用以下命令列出系统中的字型:


$ fc-list




<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<!-- ~/.fonts.conf file to configure user font access -->
<fontconfig>
<alias>
<family>serif</family>
<prefer>
<family>Bitstream Vera Serif</family>
<family>AR PL ShanHeiSun Uni</family>
<family>AR PL ShanHeiSun Uni MBE</family>
</prefer>
</alias>

<alias>
<family>sans-serif</family>
<prefer>
<family>Bitstream Vera Sans</family>
<family>AR PL ShanHeiSun Uni</family>
<family>AR PL ShanHeiSun Uni MBE</family>
</prefer>
</alias>
<alias>
<family>monospace</family>
<prefer>
<family>Bitstream Vera Sans Mono</family>
<family>AR PL ShanHeiSun Uni</family>
<family>AR PL ShanHeiSun Uni MBE</family>
</prefer>
</alias>
</fontconfig>


We put the Bitsteam serie fonts in front of our Chinese fonts because we don't want to see westen alphabetic glyphs from our Chinese fonts.

2005年5月20日

Python with Last-Modified-Time

Doing web programing sometime need to parse/create the Last-Modified-Time, Last-Modified-Since, or Date... values in the RFC2822 format. And I always forget the format string. Here we go:




>>> import time
>>> time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
'Fri, 20 May 2005 11:08:40 GMT'
>>> t = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(time.time()))
>>> t
'Fri, 20 May 2005 11:22:20 GMT'
>>> time.strptime(t, "%a, %d %b %Y %H:%M:%S GMT")
(2005, 5, 20, 11, 22, 20, 4, 140, -1)


So what these tell us? Well, the format string is:
"%a, %d %b %Y %H:%M:%S GMT"


That's it. Yes, it is GMT time, no timezone there.

2005年5月17日

Scale Gstreamer Video with GTK+

改变 Gstreamer 视频在 gtk drawingarea 的大小,比例。



Spent days on how to let a gstreamer video sink know that the video window size was changed. Finally, I realized that I missed a line of code in totem that just did this.



In order to have the video in gstreamer respect the aspect ratio of a video, you have to put the video window (normally a gtk drawingarea) inside a gtk aspectframe. However, after the aspectframe is resized to follow the video aspect ratio, you have to call video_sink.expose() in order for the video sink to resize itself. I am using gstreamer python binding, of course. So many 'have to's that are not documented!



The expose() method is an method of interface GstXOverlay. So any video sink that implements the interface should have a expose() method (gstream name is gst_x_overlay_expose(xid). Sucks, huh?)

Debian: Find programs? 寻找程序包?

Since I'm talking about Debian tricks, here is one more trick on dealing with debian packages:

Looking for program:


Sometime, people tell you to use a command to solve your problem, but you don't have this command on your system, what you do you?


You can install apt-file and run:


user$ apt-file update
user$ apt-file search command_name

"update" will download/update package lists from the apt servers. "search" will search a filename from all the packages on the apt servers, even for the files that you did not installed on your system.



想知道一个命令或者一个文件在哪个程序包里吗?比如别人让你跑一个程序,但是你发现你的系统里没有这个程序,怎么办?你可以安装 apt-file这个程序,然后跑下面的命令:


user$ apt-file update
user$ apt-file search command_name


"update" 让 apt-file 从网上下载和更新所有程序包的内容,"search"让它在程序包里搜索一个文件名,这个文件名所在的程序包有没有安装无所谓。当然这个功能其他的几个 deb/apt 应用程序也可能有,不过我几年前开始用这个,用着顺手,就没有发掘新的程序了。



当然,如果你装了 synaptic ,可能什么窍门都不用了,它全包了。

Debian: Changelog before install? 升级前看看更新记录?

Before update a package on Debian, you might want to find out what have been changed in that package. Here is how I always do:



在 Debian 里升级一个程序包的时候,你可能会想知道这个包的最新的更新记录,看看它值不值得升级。有时候只是修改说明文件里的笔误,升级没意思。我一般这么做:



reportbug package:


user$ aptitude changelog reportbug


Yes, it is simple, but you might not know before I told you. And apt-listchanges is more powerful but it is an extra package to install.



虽然简单,我不说你却未必知道。apt-listchanges 更强大些,不过它得另装一个包。

2005年5月11日

Python with Gstreamer

OK, so what if we use python and the playbin of gstreamer? Well, that can be done in a few lines:

player.py



#!/usr/bin/python
import sys, os.path
import pygtk; pygtk.require('2.0')
import gtk
import gst, gst.interfaces

def cb_eos(kele, data):
gtk.main_quit()
def cb_error(*args):
print args, args[2]

class Control:
def __init__(self, playbin, vsink,da):
self.fullscreen = False
self.play = playbin
self.vsink = vsink
self.da = da
def key_release(self, widget, event, *args):
if event.string in ['p', ' ']:
if self.play.get_state() == gst.STATE_PLAYING:
ret = self.play.set_state(gst.STATE_PAUSED)
else:
self.vsink.set_xwindow_id(self.da.window.xid)
ret = self.play.set_state(gst.STATE_PLAYING)
elif event.string == 's':
ret = self.play.set_state(gst.STATE_NULL)
elif event.string == 'q':
self.quit()
elif event.string == 'f':
if self.fullscreen:
widget.unfullscreen()
else:
widget.fullscreen()
self.fullscreen = not self.fullscreen
def quit(self, *args):
ret = self.play.set_state(gst.STATE_NULL)
gtk.main_quit()


def main():
fname = sys.argv[1]
absfname = os.path.abspath(fname)
uri = 'file://%s' % absfname

# Setup video/audio sink and playbin
vsink = gst.element_factory_make('xvimagesink')
asink = gst.element_factory_make('esdsink', 'asink')
playbin = gst.element_factory_make('playbin', 'playbin')
playbin.set_property('video-sink', vsink)
playbin.set_property('audio-sink', asink)
playbin.set_property('uri', uri)

playbin.connect('eos', cb_eos)
playbin.connect('error', cb_error)

win = gtk.Window(gtk.WINDOW_TOPLEVEL)
da = gtk.DrawingArea()
ctr = Control(playbin, vsink, da)
win.add(da)
win.connect('key-release-event', ctr.key_release)
win.connect('delete-event', ctr.quit)
win.show_all()
gtk.main()
playbin.set_state(gst.STATE_NULL)

if __name__ == '__main__':
main()
# vim:ts=8:sw=4:expandtab



You can just type


player.py test.avi


The key bindings are:





p or spacePlay/pause the movie
sStop the movie
fFullscreen/un-fullscreen
qQuit the program.



Note:



  • You have to import gst.interfaces for many methods to exist, e.g. xvimagesink.set_xwindow_id().

  • It seems that the binding between gstreamer video window and gtk+ window have to happens later. That's why we set_xwindow_id() before starting to play the movie.

  • You need to use a gtk.AspectFrame for keeping the aspect ratio of the movie.

2005年5月10日

gstreamer lauch video!

OK, here after installed the gstreamer-ffmpeg on my Debian box from deb http://debian.ressukka.net/ unstable/ , I have to test it.



I use this gst-launch command for my test. First for this clip I have, I use mplayer to find out what the codecs were used. Well, it is a .avi with msmpeg4v2 video codec and mp3/mp2 mad audio codec. Since my mplayer already installed w32codecs, I'm all set. Now we are ready, here is the command I used to play the clip with gst-launch:



  • gst-launch filesrc location=test.avi ! ffdemux_avi name=demuxer ! { queue ! ffdec_msmpeg4v2 ! ffcolorspace ! xvimagesink } { demuxer. ! queue ! mad ! esdsink }



Some notes:

  • The elements used are:









    filesrc: load video file from source.
    ffdemux_avi: demux avi into streams, from the gstreamer-ffmpeg plugin.
    queue: Since we want to play both video and audio, need to use threads for video and audio playing at the same time. queue is used to bridge a thread to other threads.
    ffdec_msmpeg4v2: well, we want to play a msmpeg4v2 video.
    ffcolorspace: convert the decoded video stream into something our video display can understand.
    xvimagesink: we use xv as our video output devicde. Other options: sdlvideosink, ximagesink. Somehow, autovideosink don't work for me.
    mad: this element handle mp3 audio codec.
    esdsink: I use esd as audio output device. Of cause alsasink should work too, if it is not used by someone else.

  • There are 2 threads as denoted by the curly bracket {...}. Since we want to play the video and audio at the sametime, we put them together using the blocks.



    To make sure our audio links from the demuxer ffdemux_avi, we assign a name to that demuxer instant, "demux". At audio thread, we link it using the name "demux." to link with our audio decoder.



    Notice the Dot->. after the "demux". Because this "demux" is not a real element, it is a ghost element, so we tell gst-lauch about this by putting a dot(.) after "demux".




Wow, what a mess! How about make it simpler? We all know gstreamer can autodetect all those codecs. OK, the element that do the autodect is decodebin. So here we go:



  • gst-launch filesrc location=test.avi ! decodebin name=debin ! { queue ! xvimagesink } { debin. ! queue ! esdsink }



That's it! We don't even need to know all the codecs things. The element "playbin" does not work for me because its video-sink/audio-sink properties only take element objects as paramenters. Cannot do that with gst-launch.



Gstreamer with gstreamer-ffmpeg and w32codecs are fun.

2005年5月9日

Compile python code? 编译蟒蛇程序?

You cannot but there are ways to hide your python code by compiling part of your program. How about a C program with a main() and use python's C API? Well,
pyrex
can do that.



All you have to do for a stand alone C program in Pyrex are:



cdef extern from "Python.h":
void Py_Initialize()
void Py_Finalize()
void PySys_SetArgv(int argc, char *argv[])

# Declare extension init function as a C function.
cdef public void initdemo()

# Define main() and declare it as "public"
cdef public int main(int argc, char *argv[]):
Py_Initialize() # Init Python environment
PySys_SetArgv(argc, argv) # Fill in sys.argv
initdemo() # Init our Pyrex generated main module.
# Do something here...
Py_Finalize() # When done, clean up Python env.





The following is a real example which actually do something. We use the re module to demostrate the power of python here:



demo.pyx:


cdef extern from "Python.h":
void Py_Initialize()
void Py_Finalize()
void PySys_SetArgv(int argc, char *argv[])

cdef extern from "stdio.h":
int printf(char *format, ...)

cdef public void initdemo()

def regex(msg):
import re
a = re.findall(r'as\w*', msg)
return a

cdef public int main(int argc, char *argv[]):
Py_Initialize()
PySys_SetArgv(argc, argv)
initdemo()
msg = 'Jump it as high as an ass, asshole!'
print 'Searching "as" from "%s"' % msg
m = regex(msg)
for i in m:
print 'Match:', i

cdef int count
count = len(m)
printf("Total: %d\n", count)
Py_Finalize()

# vim:ts=8:sw=4:expandtab



Makefile: Link with libpython.

all: demo

demo: demo.c
gcc -g -O2 -I/usr/include/python2.3 -lpython2.3 -o demo demo.c

demo.c: demo.pyx
pyrexc demo.pyx

.PHONY:
all



Run ./demo now will show you how things go. We hide everything in demo.pyx now!



As you can see, every C function that is used inside .pyx need to be explicitly declared in a cdef extern from "cheader.h": block. Other than that, Pyrex also cannot do list comprehension and generator. So do your generator or comprehension in a .py module and import to the .pyx.



And of cause if you don't write pure C inside .pyx, the resulting program will not be faster than a pure .py program.

2005年4月30日

GtkDrawingArea and GtkScrolledWindow


I saw some question asked about making flashget like progress window in gtk+. It can be simply realized using DrawingArea.



Then there is also the problem of scrolling the DrawingArea inside a ScrolledWindow. Well, this is simple. Just put a Viewport b/w the DrawingArea and the ScrolledWindow. Then do a set_size_request on the DrawingArea.





#!/usr/bin/python
import pygtk
pygtk.require('2.0')
import gtk

# 2 icons we use in our drawing area.
PICTNAME1="gdraw1.png"
PICTNAME2="gdraw2.png"
def load(pix):
'''Load GUI'''
win = gtk.Window()
gdraw = gtk.DrawingArea()
sw = gtk.ScrolledWindow()
sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
vp = gtk.Viewport()
sw.add(vp)
vp.add(gdraw)

win.add(sw)
win.resize(400, 200)
win.connect('delete-event', gtk.main_quit)
gdraw.connect('expose-event', expose_pix, pix)
win.show_all()

def expose_pix(gdraw, event, pix):
'''At expose event, actual draw graph.'''
pix1, pix2 = pix
gc = gdraw.window.new_gc()
pw = pix1.get_width()
ph = pix1.get_height()
geo = gdraw.window.get_geometry()
gw, gh = geo[2:4]
dx = dy = 0

# Draw 100 pixbuf on DrawingArea
for i in range(100):
if dx + pw > gw:
# Move down to the next line
dx = 0
dy += ph+1
if i % 7: # Draw a pix2 every 7 pix1
pixi = pix1
else:
pixi = pix2
gdraw.window.draw_pixbuf(gc, pixi, 0, 0,
dx, dy, pw, ph)
dx += pw+1
# Set the gdraw size request for Viewport
# to the height of our drawing.
gdraw.set_size_request(-1, dy+ph)

def main():
pix1 = gtk.gdk.pixbuf_new_from_file(PICTNAME1)
pix2 = gtk.gdk.pixbuf_new_from_file(PICTNAME2)
load((pix1, pix2))
gtk.main()

if __name__ == '__main__':
main()




Told you pygtk is very easy to program with.

2005年4月25日

CoolStreaming on Linux?

Doing wine + coolstreaming on Linux is smooth. Go Linux!!



It's still proprietary but... It's not like writing an open source equivalent is hard. It's just that it is not easy to find reliable media sources. There! This's my excuse for using coolstreaming. :)

Abit NF7-S2 board ethernet Linux driver.

The NF7-S2 board from Abit has a buildin ethernet chip. This is a NForce2 board but they do not use Nvidia's NIC. The one I got has this ICPlus IP100A 10/100 ethernet chip on it. Found that out from Abit's China site. Of cause if you are not a Chinese, you are not gonna find it out because this piece of information won't appare on any other Abit sites. Anyway, Under Linux, lspci shows it as:



Ethernet controller: Sundance Technology Inc: Unknown device 0200 (rev 31)



So it is basically a Sundance Ethernet Chip. It turns out that the Sundance driver come with Linux 2.6.11 does not work with the chip. I have to download the ICPlus IP100A Linux driver from ICPlus's own site.



After 'make all' and copy the sundance.ko (Why do they still call it sundance.ko?) to /lib/modules/2.6.11-01/kernel/drivers/net/ , "modprobe sundance". dah dah..., one more NIC on this box. :)



So now we know that one revision of Abit NF7 S2 mainboard use ICPlus IP100A ethernet chip which shows up as Sundance chip.