Android当中的坑

这里顺便记录下Android应用开发当中的一个一个的坑,很多时候我们都在面对这种问题,不同版本,不同厂商。。。
也许没有详尽/优雅的解决方法,但是至少问题在这里

1. SoundPool.play在Android 4.3当中没有办法looping播放

https://code.google.com/p/android/issues/detail?id=58113

2. Streaming播放声音的时候(比如AudioManager.MODE_IN_COMMUNICATION)无法切换外放
AudioManager.setMode(AudioManager.MODE_IN_CALL); // 切换成电话模式就可以切换
AudioManager.setSpeakerphoneOn(true);

3. 在某些机器上,比如插入耳机的时候无法切换外放

4.

Posted in Android | Leave a comment

Parcelable encounteredClassNotFoundException reading a Serializable object

记录下,这个问题一直无法知道合理的原因

1065 07-09 22:25:29.927 670 918 E AmStub : java.lang.RuntimeException: Parcelable encounteredClassNotFoundException reading a Serializable object (name = xx.oo.MySerializableObject)
1066 07-09 22:25:29.927 670 918 E AmStub : at android.os.Parcel.readSerializable(Parcel.java:2148)
1067 07-09 22:25:29.927 670 918 E AmStub : at android.os.Parcel.readValue(Parcel.java:2016)
1068 07-09 22:25:29.927 670 918 E AmStub : at android.os.Parcel.readMapInternal(Parcel.java:2226)
1069 07-09 22:25:29.927 670 918 E AmStub : at android.os.Bundle.unparcel(Bundle.java:223)
1070 07-09 22:25:29.927 670 918 E AmStub : at android.os.Bundle.containsKey(Bundle.java:271)
1071 07-09 22:25:29.927 670 918 E AmStub : at android.content.Intent.hasExtra(Intent.java:4414)
1072 07-09 22:25:29.927 670 918 E AmStub : at com.android.server.am.c.a(Unknown Source)
1073 07-09 22:25:29.927 670 918 E AmStub : at com.android.server.am.AmSmartShowStub.checkStartActivity(Unknown Source)
1074 07-09 22:25:29.927 670 918 E AmStub : at com.android.server.am.ActivityManagerService.checkStartActivity(ActivityManagerService.java:3015)
1075 07-09 22:25:29.927 670 918 E AmStub : at com.android.server.am.ActivityManagerService.startActivityAsUser(ActivityManagerService.java:3224)
1076 07-09 22:25:29.927 670 918 E AmStub : at com.android.server.am.ActivityManagerService.startActivity(ActivityManagerService.java:3213)
1077 07-09 22:25:29.927 670 918 E AmStub : at android.app.ActivityManagerNative.onTransact(ActivityManagerNative.java:144)
1078 07-09 22:25:29.927 670 918 E AmStub : at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:1968)
1079 07-09 22:25:29.927 670 918 E AmStub : at android.os.Binder.execTransact(Binder.java:351)
1080 07-09 22:25:29.927 670 918 E AmStub : at dalvik.system.NativeStart.run(Native Method)
1081 07-09 22:25:29.927 670 918 E AmStub : Caused by: java.lang.ClassNotFoundException: xx.oo.MySerializableObject
1082 07-09 22:25:29.927 670 918 E AmStub : at java.lang.Class.classForName(Native Method)
1083 07-09 22:25:29.927 670 918 E AmStub : at java.lang.Class.forName(Class.java:217)
1084 07-09 22:25:29.927 670 918 E AmStub : at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:2279)
1085 07-09 22:25:29.927 670 918 E AmStub : at java.io.ObjectInputStream.readNewClassDesc(ObjectInputStream.java:1638)
1084 07-09 22:25:29.927 670 918 E AmStub : at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:2279)
1085 07-09 22:25:29.927 670 918 E AmStub : at java.io.ObjectInputStream.readNewClassDesc(ObjectInputStream.java:1638)
1086 07-09 22:25:29.927 670 918 E AmStub : at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:658)
1087 07-09 22:25:29.927 670 918 E AmStub : at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1781)
1088 07-09 22:25:29.927 670 918 E AmStub : at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:762)
1089 07-09 22:25:29.927 670 918 E AmStub : at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1981)
1090 07-09 22:25:29.927 670 918 E AmStub : at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1938)
1091 07-09 22:25:29.927 670 918 E AmStub : at android.os.Parcel.readSerializable(Parcel.java:2142)
1092 07-09 22:25:29.927 670 918 E AmStub : ... 14 more
1093 07-09 22:25:29.927 670 918 E AmStub : Caused by: java.lang.NoClassDefFoundError: xx/oo/MySerializableObject
1094 07-09 22:25:29.927 670 918 E AmStub : ... 24 more
1095 07-09 22:25:29.927 670 918 E AmStub : Caused by: java.lang.ClassNotFoundException: Didn't find class "xx.oo.MySerializableObject" on path: DexPathList[[zip file "/system/framework/mediatek-op.jar"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
1096 07-09 22:25:29.927 670 918 E AmStub : at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:53)
1097 07-09 22:25:29.927 670 918 E AmStub : at java.lang.ClassLoader.loadClass(ClassLoader.java:501)
1098 07-09 22:25:29.927 670 918 E AmStub : at java.lang.ClassLoader.loadClass(ClassLoader.java:461)
1099 07-09 22:25:29.927 670 918 E AmStub : ... 24 more
1100 07-09 22:25:29.934 670 918 I ActivityManager: START u0 {flg=0x10000000 cmp=xx.oo/xx.oo.MyActivity (has extras) contextId=2722, taskId=2306 } from pid 23461

Posted in Android | Leave a comment

Tips about Android Studio

还是开一篇记录Android Studio有关的各种诡异问题。
首先感谢一下,一直以Preview状态出现的AS最近终于是Beta了,普天同庆!
AS有好也有不好,但是感觉就是用了它就回不去Eclipse了。

之前基本在版本升级的时候都会或多或少出现点问题,有的坑了很久,记录分享下,避免在这上面浪费时间!

1. 一直提示refreshing/configure project,很长时间都不会结束。
经过观察这个是在现在一些依赖包,有800多M,网速慢的话估计很难等到下载完成的时候。有个解决办法就是在Preferences当中选择Gradle为Offline work模式。
当然也可以全局配置Offline work,打开$HOME/.gradle/init.gradle,如果没有该文件就创建一个,然后在里面添加

allprojects {
    repositories {
        .......
    }

    gradle.startParameter.setOffline(true)
}

主要就是这句gradle.startParameter.setOffline(true)啦,不是很明白的可以去网络上搜索了解下,
http://www.gradle.org/docs/current/userguide/init_scripts.html
http://www.gradle.org/docs/current/javadoc/org/gradle/StartParameter.html
如果发现创建项目之后,已经处于这种卡住的状态,可以断开网络链接,一会儿这种卡住状态就会消失了。

2. 2014-07-01 10:24:55,919 [1292862] INFO - .project.GradleProjectImporter - The project is using an unsupported version of the Android Gradle plug-in (0.11.2)
鼠标移动到build.gradle当中的classpath值上面,根据提示修改就可以

    dependencies {
        classpath 'com.android.tools.build:gradle:0.11.+'
    }

然后执行gradlew clean assembleDefaultFlavor或者直接clean build工程,应该就可以解决。

3. 还有个东西叫做代理,关键的时候可以用上。

Posted in Misc | Tagged | Leave a comment

Building Android apps with Jenkins CI

在前东家,工程相关的都有专门的组来做,比如自动化的代码审查,编译,发布,测试,缺陷追踪等等系统。。。甚至当时就有人说就XXX的很多系统,即使司龄很大的员工都不一定都能使用的很熟练,因为实在是太多了。。。相反的是现在都没有,万事都需要自己动手,不过也还好,可以挑选些真的是非常有帮助的自动化系统来帮助我们提高工作效率(比如之前我一直都是不大记住写周报,其实我觉得那应该是小时报,不过还好,我当时的老大和部门经理对这个要求没有那么严格,我也不是偷懒的员工,所以基本是马马虎虎过了,但是我还是比较喜欢例如某些任务管理系统,用任务管理系统主要是防止自己忘记,一些大任务可以拆分成小任务,并且标记完成状况,或者是你在任务上撰写出你自己的意见/建议和老大进行沟通),今天就以自动化编译系统为起点来逐步搭建/定制出一个比较理想的持续集成系统。。。当然这是一个长期的任务,不是说它非常难,只是自己时间也不是特别多,会先挑些目前比较重要的东西来加入进来(building,lint,findbugs,monkey)。。。
今天先说编译系统,而且先是看基于Ant的,假设源码是放在GitHub上,比如https://github.com/guohai/and-expandable-listview/这个项目,我希望我的系统在每隔一定时间或者每次代码提交之后自己启动一次编译,第一这可以排错,防止开发人员不小心漏提交代码,或者merge代码的时候出错,尽早的发现这类比较低级的错误,编译系统编译失败会立刻邮件通知你,而不是你在本地编译测试了很多遍,信心满满的把代码提交(当然有人说会有人工代码审查,但是人工也不一定能发现所有错误,特别是编译错误),回家睡觉了,另外一个时区的同事刚开始新的一天,检出你的代码,发现根本编译不通过,那么他是该打你的电话叫醒你修改代码呢?还是等你第二天来修改呢?明显他两样都不想,所以最好是你保证自己提交的代码都是至少可以编译通过的(会有人说这么简单的事情会做不到吗?事实是当你很多人协同工作的时候,这个事情确实是有可能发生,很多次在XXX公司经历过一两天编译系统无法出一个可以通过的版本,当然这不是在黑前东家,只是事情确实有,而且公司也在逐步改用好一点的编译方式减少这种一个编译错误导致整个系统都无法编译出ROM的窘境);第二这个可以利用一些工具直接将编译的成果推送到自动化测试的服务里面完成基础的自动化测试,这样岂不是很好,减少些不必要的人工操作;第三有权限的人可以随时下载一个他需要的版本程序来安装运行,而不需要让你给他邮件发一个版本或者他自己安装编译环境,检出代码来完成编译。可能还有很多理由来需要这样一个系统,不过这里已经够了。。。
Jenkins的安装,基本使用就不说了,具体查看https://wiki.jenkins-ci.org/display/JENKINS/Use+Jenkins上面,我这里主要安装了一个GitHub插件,在Manage Jenkins->Manage Plugins当中可以很方便的完成,因为我本地都有Android SDK, Ant, Java等等一些基础环境,而且这些都已经在PATH当中,所以集成起来就很简单。然后就是创建任务, Jenkins当中称为job,然后就是配置该job,比如GitHub项目位置,仓库地址,keystore for release build,编译预处理,归档编译产物,以及触发编译条件,如下图:
Screen Shot 2014-01-27 at 12.00.47 AM
Screen Shot 2014-01-27 at 12.01.53 AM
Screen Shot 2014-01-27 at 12.14.36 AM
Screen Shot 2014-01-27 at 12.15.05 AM
Screen Shot 2014-01-27 at 12.34.11 AM
Screen Shot 2014-01-27 at 12.43.14 AM
这样就基本可以搭建出一个自动化的编译系统了,后面有机会来研究下基于Gradle的,毕竟这才是Google目前主导的方向。
当然你可以根据自己的需要调节更改配置来达到想要自己的目的,比如GitHub有提交就触发编译,只归档特定名字的文件等等,是否删除原有归档,如果新的版本有成功编译出来的话,等等可能还有更多的技巧。

洗完个澡,顺便试了下Gradle的,你可以在Jenkins当中下载Gradle插件,也可以不用专门下载,自己写个简单的脚本完成,比如我这里就是:
Screen Shot 2014-02-06 at 12.25.49 AM
而且也只有上述这一段不一样,其他都跟Ant的基本类似,不过生成需要归档的apk的路径可能需要改改,这里有个硬编码的ANDROID_HOME,目前没有发现比较好的解决方法,如果您知道如何解决,不吝赐教。在Ant版本中,我们sdk.dir是通过android update project生成的,但是在Gradle的项目当中似乎无法生成,没有SDK路径就会编译出错,比如:

* Where:
Build file '/Users/guohai/.jenkins/jobs/Gradle-Proj-Test/workspace/xxx/build.gradle' line: 9

* What went wrong:
A problem occurred evaluating project ':beckon'.
> SDK location not found. Define location with sdk.dir in the local.properties file or with an ANDROID_HOME environment variable.
于是就先硬编码这个ANDROID_HOME变量先跳过这一问题,有空再来研究看看。
1
export ANDROID_HOME=/Users/guohai/Dev/android-sdk-macosx
chmod +x ./gradlew
./gradlew clean assembleDefaultFlavorDebug

当然这gradlew后面跟的task可以自己调整,比如debug版本或者release版本,或者编译子项目(./gradlew :sub-project:clean,sub-project就是实际子项目的名字)。

P.S.
通知邮件发送使用Jenkins Email Extension Plugin
不过在配置的时候出现
501 mail from address must be same as authorization user
参考http://bbs.csdn.net/topics/390353671设置System Admin e-mail address就可以了。

Posted in Misc | Tagged | Leave a comment

小结二零一三

似乎已经过了年终小结的时间了,不过迟点就迟点吧,今年可以典型的分为上半年和下半年,上半年在为公司加班(已经是前东家了),下半年在为自己加班,还是有点忙,不过也没有什么好看的结果,人生很多时候是努力了不一定有明显的结果的。去年的愿望有实现了部分,乱七八糟的读了一些书,C/C++,还有部分多媒体知识有了些进步,有些东西看起来比较复杂和庞大,不过当你花了很多时间在上面,并且突然理解的时候,这是最开心的,比通常在公司里实现了一个又一个功能,解了一个又一个bug的感觉要好太多,当然我知道工作只是工作,如果你的工作不是你的兴趣所在,那你的路就是把它做好或者换一份工作,或者如果你无法换工作的时候就只是做好它然后自己做自己爱好的事情,老板给你的任务太重你需要表达出自己的状况,要不然很大的可能就是你被更多的工作包围着,这当然不是教你偷懒,万事都有一个度的问题。你若热爱它,怎么努力都不过分,若不热爱,就要努力去寻找到自己的胡萝卜。。。当然一些事情也没有能做的很好,比如多回去看看爸妈,比如多学习深入点的多媒体/信息论的知识,这也成为了2014的目标。具体点来说今年可能会更多的关注在语音这方面,因为刚刚加入了一家这方面的创业公司,估计还是一样的会比较忙碌,不过还是尽量不要让自己完全被工作给吞噬了。。。2014还是力争多读点书,语音方面理解AudioFlinger,至少熟悉一种CODEC。还有一个个人方面的愿望就是能有一个可以共度一生的人,在一个充满阳光的下午,两个人坐一起,一个人在写代码,一个人在看书,或者有时间两个人出去吃喝玩乐,平平淡淡,普普通通就好,不知道这是不是一种奢求!

Posted in Life | 2 Comments

Python连接MySQL数据库

UPDATE: 其实目前没有用到多少Python知识来写,基本都是直接调用的已有的命令来完成备份和邮件通知功能 @@
为了写个简单的备份程序,我决定使用Python来实现,几年前有看过点Python,今天就来试试看。通常我们连接数据库会用到对应的连接API,比如Java的JDBC(Java_Database_Connectivity‎),顶多中间增加个连接池之类的,因为我们这里很简单,所以只需要一个类似的API,我们这里用MySQL-python来完成。先安装这个东西,我采用1.2.3版本,当然我这里宿主机器是CentOS。

[guohai@Knight-on-the-Cloud installation]$ wget http://sourceforge.net/projects/mysql-python/files/mysql-python/1.2.3/MySQL-python-1.2.3.tar.gz/download
[guohai@Knight-on-the-Cloud installation]$ tar xvf MySQL-python-1.2.3.tar.gz
[guohai@Knight-on-the-Cloud installation]$ cd MySQL-python-1.2.3

vim site.cfg

# The path to mysql_config.
# Only use this if mysql_config is not on your PATH, or you have some weird
# setup that requires it.
mysql_config = /usr/bin/mysql_config

正如这上面所说,之前我安装MySQL有改动默认位置,所以这里要单独指定下。

[guohai@Knight-on-the-Cloud MySQL-python-1.2.3]$ python setup.py build

if errors("no setuptools") showing

cd ..
wget https://pypi.python.org/packages/2.6/s/setuptools/setuptools-0.6c10-py2.6.egg --no-check-certificate
sh setuptools-0.6c10-py2.6.egg

然后

cd MySQL-python-1.2.3

[guohai@Knight-on-the-Cloud MySQL-python-1.2.3]$ python setup.py build

if errors("Python.h: No such file or directory") showing

[root@Knight-on-the-Cloud installation]# yum -y install python-devel

继续

[guohai@Knight-on-the-Cloud MySQL-python-1.2.3]$ python setup.py build
running build
running build_py
copying MySQLdb/release.py -> build/lib.linux-x86_64-2.6/MySQLdb
running build_ext
building '_mysql' extension
gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -DNDEBUG -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -fPIC -Dversion_info=(1,2,3,'final',0) -D__version__=1.2.3 -I/usr/include/mysql -I/usr/include/python2.6 -c _mysql.c -o build/temp.linux-x86_64-2.6/_mysql.o -g -pipe -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -fno-strict-aliasing -fwrapv -fPIC -DUNIV_LINUX -DUNIV_LINUX
_mysql.c:36:23: error: my_config.h: No such file or directory
_mysql.c:38:19: error: mysql.h: No such file or directory
_mysql.c:39:26: error: mysqld_error.h: No such file or directory
_mysql.c:40:20: error: errmsg.h: No such file or directory
[root@Knight-on-the-Cloud include]# yum -y install mysql-devel

[root@Knight-on-the-Cloud mysql]# pwd
/usr/include/mysql
[guohai@Knight-on-the-Cloud MySQL-python-1.2.3]$ python setup.py build
running build
running build_py
copying MySQLdb/release.py -> build/lib.linux-x86_64-2.6/MySQLdb
running build_ext
building '_mysql' extension
gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -DNDEBUG -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -fPIC -Dversion_info=(1,2,3,'final',0) -D__version__=1.2.3 -I/usr/include/mysql -I/usr/include/python2.6 -c _mysql.c -o build/temp.linux-x86_64-2.6/_mysql.o -g -pipe -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -fno-strict-aliasing -fwrapv -fPIC -DUNIV_LINUX -DUNIV_LINUX
In file included from /usr/include/mysql/my_config.h:14,
                 from _mysql.c:36:
/usr/include/mysql/my_config_x86_64.h:1082:1: warning: "HAVE_WCSCOLL" redefined
In file included from /usr/include/python2.6/pyconfig.h:6,
                 from /usr/include/python2.6/Python.h:8,
                 from pymemcompat.h:10,
                 from _mysql.c:29:
/usr/include/python2.6/pyconfig-64.h:808:1: warning: this is the location of the previous definition
gcc -pthread -shared build/temp.linux-x86_64-2.6/_mysql.o -L/usr/lib64/mysql -L/usr/lib64 -lmysqlclient_r -lz -lpthread -lcrypt -lnsl -lm -lpthread -lssl -lcrypto -lpython2.6 -o build/lib.linux-x86_64-2.6/_mysql.so

编译好了就安装

[root@Knight-on-the-Cloud MySQL-python-1.2.3]# python setup.py install
running install
running bdist_egg
running egg_info
writing MySQL_python.egg-info/PKG-INFO
writing top-level names to MySQL_python.egg-info/top_level.txt
writing dependency_links to MySQL_python.egg-info/dependency_links.txt
reading manifest file 'MySQL_python.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
warning: no files found matching 'MANIFEST'
warning: no files found matching 'ChangeLog'
warning: no files found matching 'GPL'
writing manifest file 'MySQL_python.egg-info/SOURCES.txt'
installing library code to build/bdist.linux-x86_64/egg
running install_lib
running build_py
copying MySQLdb/release.py -> build/lib.linux-x86_64-2.6/MySQLdb
running build_ext
creating build/bdist.linux-x86_64
creating build/bdist.linux-x86_64/egg
copying build/lib.linux-x86_64-2.6/_mysql.so -> build/bdist.linux-x86_64/egg
copying build/lib.linux-x86_64-2.6/_mysql_exceptions.py -> build/bdist.linux-x86_64/egg
creating build/bdist.linux-x86_64/egg/MySQLdb
copying build/lib.linux-x86_64-2.6/MySQLdb/__init__.py -> build/bdist.linux-x86_64/egg/MySQLdb
copying build/lib.linux-x86_64-2.6/MySQLdb/cursors.py -> build/bdist.linux-x86_64/egg/MySQLdb
copying build/lib.linux-x86_64-2.6/MySQLdb/connections.py -> build/bdist.linux-x86_64/egg/MySQLdb
copying build/lib.linux-x86_64-2.6/MySQLdb/times.py -> build/bdist.linux-x86_64/egg/MySQLdb
copying build/lib.linux-x86_64-2.6/MySQLdb/release.py -> build/bdist.linux-x86_64/egg/MySQLdb
creating build/bdist.linux-x86_64/egg/MySQLdb/constants
copying build/lib.linux-x86_64-2.6/MySQLdb/constants/REFRESH.py -> build/bdist.linux-x86_64/egg/MySQLdb/constants
copying build/lib.linux-x86_64-2.6/MySQLdb/constants/CLIENT.py -> build/bdist.linux-x86_64/egg/MySQLdb/constants
copying build/lib.linux-x86_64-2.6/MySQLdb/constants/FIELD_TYPE.py -> build/bdist.linux-x86_64/egg/MySQLdb/constants
copying build/lib.linux-x86_64-2.6/MySQLdb/constants/FLAG.py -> build/bdist.linux-x86_64/egg/MySQLdb/constants
copying build/lib.linux-x86_64-2.6/MySQLdb/constants/__init__.py -> build/bdist.linux-x86_64/egg/MySQLdb/constants
copying build/lib.linux-x86_64-2.6/MySQLdb/constants/CR.py -> build/bdist.linux-x86_64/egg/MySQLdb/constants
copying build/lib.linux-x86_64-2.6/MySQLdb/constants/ER.py -> build/bdist.linux-x86_64/egg/MySQLdb/constants
copying build/lib.linux-x86_64-2.6/MySQLdb/converters.py -> build/bdist.linux-x86_64/egg/MySQLdb
byte-compiling build/bdist.linux-x86_64/egg/_mysql_exceptions.py to _mysql_exceptions.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/__init__.py to __init__.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/cursors.py to cursors.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/connections.py to connections.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/times.py to times.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/release.py to release.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/constants/REFRESH.py to REFRESH.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/constants/CLIENT.py to CLIENT.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/constants/FIELD_TYPE.py to FIELD_TYPE.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/constants/FLAG.py to FLAG.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/constants/__init__.py to __init__.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/constants/CR.py to CR.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/constants/ER.py to ER.pyc
byte-compiling build/bdist.linux-x86_64/egg/MySQLdb/converters.py to converters.pyc
creating stub loader for _mysql.so
byte-compiling build/bdist.linux-x86_64/egg/_mysql.py to _mysql.pyc
creating build/bdist.linux-x86_64/egg/EGG-INFO
copying MySQL_python.egg-info/PKG-INFO -> build/bdist.linux-x86_64/egg/EGG-INFO
copying MySQL_python.egg-info/SOURCES.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
copying MySQL_python.egg-info/dependency_links.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
copying MySQL_python.egg-info/top_level.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
writing build/bdist.linux-x86_64/egg/EGG-INFO/native_libs.txt
zip_safe flag not set; analyzing archive contents...
creating dist
creating 'dist/MySQL_python-1.2.3-py2.6-linux-x86_64.egg' and adding 'build/bdist.linux-x86_64/egg' to it
removing 'build/bdist.linux-x86_64/egg' (and everything under it)
Processing MySQL_python-1.2.3-py2.6-linux-x86_64.egg
Copying MySQL_python-1.2.3-py2.6-linux-x86_64.egg to /usr/lib64/python2.6/site-packages
Adding MySQL-python 1.2.3 to easy-install.pth file

Installed /usr/lib64/python2.6/site-packages/MySQL_python-1.2.3-py2.6-linux-x86_64.egg
Processing dependencies for MySQL-python==1.2.3
Finished processing dependencies for MySQL-python==1.2.3

接着再来试下

[guohai@Knight-on-the-Cloud MySQL-python-1.2.3]$ python
Python 2.6.6 (r266:84292, Jul 10 2013, 22:48:45)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import MySQLdb
/usr/lib64/python2.6/site-packages/MySQL_python-1.2.3-py2.6-linux-x86_64.egg/_mysql.py:3: UserWarning: Module _mysql was already imported from /usr/lib64/python2.6/site-packages/MySQL_python-1.2.3-py2.6-linux-x86_64.egg/_mysql.pyc, but /home/guohai/apps/installation/MySQL-python-1.2.3 is being added to sys.path

上面这些UserWarning是说MySQL模块已经引入了,但是你在的目录又被加入到sys.path,也就是重复引入。这个时候你只要跳出我们刚刚编译模块的目录就好了。
一切都正常之后我们来写个简单的程序试试看。

[guohai@Knight-on-the-Cloud npc]$ python
Python 2.6.6 (r266:84292, Jul 10 2013, 22:48:45)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import MySQLdb

>>> help(MySQLdb) // 不懂的话就可以这样查询

>>> conn = MySQLdb.connect('localhost', 'wp_user', 'wp_passwd', 'wp_db')
>>> cur = conn.cursor()
>>> cur.execute('select user_login, user_registered from wp_users')
1L
>>> rst = cur.fetchall()
>>> print rst
(('admin', datetime.datetime(2011, 3, 19, 0, 8, 9)),)
>>> cur.close()
>>> conn.close()

与我们通过

[guohai@Knight-on-the-Cloud npc]# mysql -h localhost -u wp_user -p

查询出来的一样

mysql> select * from wp_users \G
*************************** 1. row ***************************
                 ID: 1
         user_login: admin
          user_pass: #CLASSIFIED#
      user_nicename: #CLASSIFIED#
         user_email: #CLASSIFIED#
           user_url: #CLASSIFIED#
    user_registered: 2011-03-19 00:08:09
user_activation_key: #CLASSIFIED#
        user_status: #CLASSIFIED#
       display_name: #CLASSIFIED#
1 row in set (0.00 sec)

也就是说我们简单的连接数据库成功了,到此基础环境算是搭建好了。

另外简单写了个备份程序(https://github.com/guohai/my-conf/blob/master/wp_backup_npc.py),待完善。。。

然后编写一个crontab就可以让它定时执行(不会crontab的请参见http://linux.vbird.org/linux_basic/redhat6.1/linux_11cron.php)

9 16 * * 0 cd /home/guohai/apps/npc/ && python wp_backup_npc.py &> /dev/null

就是在每周日的16点9分会运行一次后面的命令(跳转到npc文件夹,执行这个python命令,如果有一些信息输出的话就把信息重定向到/dev/null,如果有信息输出而不重定向这些信息的话,crontab就会认为这个命令是执行失败的,所以就会给你的邮箱发封提醒邮件,时间久了你的/var/spool/mail/账户下就会有很多邮件,所以这里我把一切输出信息都重定向到/dev/null了)。

当然你也可以阅读简单的介绍了解crontab脚本如何编写,如下:

# Edit this file to introduce tasks to be run by cron.
#
# Each task to run has to be defined through a single line
# indicating with different fields when the task will be run
# and what command to run for the task
#
# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use '*' in these fields (for 'any').#
# Notice that tasks will be started based on the cron's system
# daemon's notion of time and timezones.
#
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
#
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h dom mon dow command

Posted in Misc | Tagged | Leave a comment

迁移博客到Digital Ocean

搜了下据说性价比还可以的VPS,似乎我在上海ping值很高,用用看吧(可以按小时付费)。

注册账号,创建VPS,拿到root用户

ssh root@162.243.117.95

创建常用账户

ssh guohai@162.243.117.95

拿到之后当然先装些基础的软件,比如FTP服务,SSL等等,这个看自己的爱好。
摸出我当年做各种MIS的经验,什么Nginx/MySQL/PHP,不过PHP我之前没有弄过,照着网络上简单的搞搞。

[root@Knight-on-the-Cloud ~]# yum -y install vsftpd
参照

http://my.oschina.net/u/130017/blog/15229

基础配置

local_enable=YES
ascii_upload_enable=YES
ascii_download_enable=YES
write_enable=YES

然后就是MySQL
[root@Knight-on-the-Cloud log]# yum -y install mysql-server

[root@Knight-on-the-Cloud log]# service mysqld start
Initializing MySQL database: WARNING: The host 'Knight-on-the-Cloud' could not be looked up with resolveip.
This probably means that your libc libraries are not 100 % compatible
with this binary MySQL version. The MySQL daemon, mysqld, should work
normally with the exception that host name resolving will not work.
This means that you should use IP addresses instead of hostnames
when specifying MySQL privileges !
Installing MySQL system tables...
OK
Filling help tables...
OK

To start mysqld at boot time you have to copy
support-files/mysql.server to the right place for your system

PLEASE REMEMBER TO SET A PASSWORD FOR THE MySQL root USER !
To do so, start the server, then issue the following commands:

/usr/bin/mysqladmin -u root password 'new-password'
/usr/bin/mysqladmin -u root -h Knight-on-the-Cloud password 'new-password'

Alternatively you can run:
/usr/bin/mysql_secure_installation

which will also give you the option of removing the test
databases and anonymous user created by default. This is
strongly recommended for production servers.

See the manual for more instructions.

You can start the MySQL daemon with:
cd /usr ; /usr/bin/mysqld_safe &

You can test the MySQL daemon with mysql-test-run.pl
cd /usr/mysql-test ; perl mysql-test-run.pl

Please report any problems with the /usr/bin/mysqlbug script!

[ OK ]
Starting mysqld: [ OK ]

这样装好之后就记住你MySQL的root密码啦,记得所有都是最小权限原则,不用的就不需要开。

移动MySQL数据文件位置,当然先得停止MySQL服务。
这几个命令都是在OS的root权限下进行的。
mkdir -p /usr/local/mysql
cp -afir /var/lib/mysql/* /usr/local/mysql

vim /etc/my.cnf

[mysqld]
datadir=/usr/local/mysql
socket=/usr/local/mysql/mysql.sock
default-character-set=utf8
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0

[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

[mysql]
default-character-set=utf8

vim /etc/init.d/mysqld
datadir=/usr/local/mysql

启动MySQL,并登录试试。
mysql -h 127.0.0.1 -P 3306 -u root -p
或者用
mysql -u root -p
来登录

最后还是创建一个软链接,主要是后面PHP程序连过来的时候是找的默认这个地方(/var/lib/mysql/mysql.sock)
[root@Knight-on-the-Cloud mysql]# mkdir -p /var/lib/mysql/
[root@Knight-on-the-Cloud mysql]# ln -s /usr/local/mysql/mysql.sock /var/lib/mysql/mysql.sock

如果数据库能启动,看看/var/log/mysqld.log里面日志都正常的话,这步就应该好了。

安装Nginx

https://www.digitalocean.com/community/articles/how-to-compile-nginx-from-source-on-an-centos-6-4-x64-vps

编译参数如下

./configure \
--user=nginx                          \
--group=nginx                         \
--prefix=/etc/nginx                   \
--sbin-path=/usr/sbin/nginx           \
--conf-path=/etc/nginx/nginx.conf     \
--pid-path=/var/run/nginx.pid         \
--lock-path=/var/run/nginx.lock       \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--with-http_gzip_static_module        \
--with-http_stub_status_module        \
--with-http_ssl_module                \
--with-pcre                           \
--with-file-aio                       \
--with-http_realip_module             \
--without-http_scgi_module            \
--without-http_uwsgi_module           \
--without-http_fastcgi_module

注意,如果你弄好这些启动的话,发现
nginx: [emerg] unknown directive "fastcgi_pass"
nginx不要带这个参数--without-http_fastcgi_module编译,这样就表示Nginx集成了fastCGI的功能,也就不需要使用spawn-fcgi等等。

配置/etc/nginx/nginx.conf
types_hash_bucket_size 64;
server_names_hash_bucket_size 128;

加入
include /etc/nginx/conf.d/*.conf;
到/etc/nginx/nginx.conf

安装PHP

https://www.digitalocean.com/community/articles/how-to-install-linux-nginx-mysql-php-lemp-stack-on-centos-6

如果没有启用Nginx或者PHP的fastCGI,就只能安装

http://www.lighttpd.net/download/spawn-fcgi-1.6.3.tar.gz

了,因为我们在编译Nginx的时候有http_fastcgi_module,所以就不需要这个了

Nginx出现403,
原因http://www.linuxidc.com/Linux/2013-06/85625.htm

vim /etc/nginx/nginx.conf
user guohai;

root 18137 0.0 0.2 44552 1092 ? Ss 13:30 0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
guohai 18139 0.0 0.3 44956 1708 ? S 13:30 0:00 nginx: worker process
guohai 18140 0.0 0.3 44956 1708 ? S 13:30 0:00 nginx: worker process
guohai 18141 0.0 0.3 44956 1680 ? S 13:30 0:00 nginx: worker process
guohai 18142 0.0 0.3 44956 1692 ? S 13:30 0:00 nginx: worker process

访问目录权限
[guohai@Knight-on-the-Cloud html]$ ls -l
total 4
drwxrwxr-- 2 guohai guohai 4096 Nov 27 11:07 guoh.org

[guohai@Knight-on-the-Cloud guoh.org]$ ls -l
total 8
-rw-rw-r-- 1 guohai guohai 0 Nov 27 13:37 hello.txt
-rw-rw-r-- 1 guohai guohai 612 Nov 27 11:33 index.html
-rw-rw-r-- 1 guohai guohai 617 Nov 27 11:05 info.php

PHP页面出现,
404, No input file specified.
vim /etc/php-fpm.d/www.conf
Unix user/group of processes里面用户和组控制的和所需要访问文件属于同一个用户和组就好,我这里都改成guohai。

root 18409 0.0 0.7 125452 3952 ? Ss 15:09 0:00 php-fpm: master process (/etc/php-fpm.conf)
guohai 18410 0.0 1.0 126088 5208 ? S 15:09 0:00 php-fpm: pool www
guohai 18411 0.0 1.0 125972 5216 ? S 15:09 0:00 php-fpm: pool www
guohai 18412 0.0 0.8 125972 4136 ? S 15:09 0:00 php-fpm: pool www
guohai 18413 0.0 0.8 125452 4124 ? S 15:09 0:00 php-fpm: pool www
guohai 18414 0.0 1.0 126088 5208 ? S 15:09 0:00 php-fpm: pool www

所以这两个诡异的问题都是权限问题

也就是我们这里Nginx和PHP的运行用户都是guohai,文件所属也都是guohai,这样就不会出任何问题了(Nginx或者PHP的master process都是运行在daemon当中,所以这里是root),当然可能正式服务器上应该部署在www这种权限比较少的用户下面。

我这里是迁移工作,所以数据库和程序文件我都有导入出来,现在只需要导入数据库,把文件放到指定的目录就好。如果是全新安装的话,只要创建好数据库和用户就好,安装WP的时候会自动生成数据库。
创建数据库和用户

CREATE DATABASE wp_db;
CREATE USER wp_user@localhost;
SET PASSWORD FOR wp_user@localhost= PASSWORD("YOUR_STRONG_PASSWORD");
GRANT ALL PRIVILEGES ON wp_db.* TO wp_user@localhost IDENTIFIED BY 'YOUR_STRONG_PASSWORD';
FLUSH PRIVILEGES;

当然你如果熟悉MySQL的话,可以细分权限,给够就行了。

https://www.digitalocean.com/community/articles/how-to-install-wordpress-with-nginx-on-centos-6--2

WordPress在Nginx上开启固定链接(permalinks),参见的是http://blog.dighost.me/archives/3233.html,网络上很多方法都不成功,不知道到底是对视错,我的是WP放在子目录(lifelog)下面,所以

    location / {
        index index.php  index.html index.htm;
        try_files $uri $uri/ /index.php?$args;
#       try_files and rewrite, which is better?
#        if (-f $request_filename/index.html) {
#            rewrite (.*) $1/index.html break;
#        }
#        if (-f $request_filename/index.php) {
#            rewrite (.*) $1/index.php;
#        }
#        if (!-f $request_filename) {
#            rewrite (.*) /index.php;
#        }
    }

    location /lifelog/ {
        index index.php  index.html index.htm;
        try_files $uri $uri/ /lifelog/index.php?$args;
    }

也就是我只修改了location /lifelog/的内容,并且index后面有这么多,部分人只放了index.php,但是我这样就会出现404,如果写全就能工作,所以。。。

所有东西都弄好之后,然后可以隐藏掉Nginx/PHP版本号,避免软件漏洞出现后,攻击者很容易知道你的系统软件版本,然后直接拿下你的机器,不过我这台机器应该没有人感兴趣。
参见http://wangye.org/blog/archives/352/
分别在/etc/nginx/nginx.conf增加
http {
# ...省略一些配置
server_tokens off;
}

在/etc/php.ini关闭expose_php,即修改为
expose_php = Off
或者你自己编译源码,尽量隐藏掉你不想让别人看到的信息。

P.S.
运行了几个小时,发现有时候还是会有404,先用用看有没有什么比较严重的问题,备份很重要,有空来写几个脚本去定时导出和打包数据库和文件。
需要注册的同学可以使用https://www.digitalocean.com/?refcode=7340d6d347a6这个优惠码,你好我也好,哈哈哈 /偷笑

跑了会儿就觉得站点反应慢,看了下
top发现CPU很空,Memory几乎满掉了
ps -A --sort -rss -o comm,pmem,pcpu | uniq -c | head -15
发现php-fpm占了很多内存
ps aux | grep php-fpm
网上搜了下,遇到php-fpm开的多的人很多,而每个php-fpm进程大约占用20M左右的内存,所以内存很快就会耗光了

http://www.perfgeeks.com/?p=599

http://www.ha97.com/4339.html

这两篇博客解释了php-fpm的工作方式,我这里是动态的,然后我这里VPS的内存比较小,而max_spare_servers默认是35个,所以就会耗非常多的内存,于是我尝试将这个值改小。
vim /etc/php-fpm.d/www.conf
修改pm.max_spare_servers
再跑跑看。

P.P.S. 难道为了搞个VPS搭个小站,还要逼我学习如何调优Nginx/PHP/MySQL?突然发现还是我那个40人民币/年的空间比较好,ping值也低,也没有这么多事,可惜它经常不出声响的就处于维护状态,让人比较头疼。用了这个VPS,现在如果坏了得自己修了。。。

P.P.P.S 发现这事停不下来了
一些Nginx简单的配置
vim /etc/nginx/nginx.conf

Nginx上开启gzip选项

    gzip  on;
    gzip_min_length 1k;
    gzip_buffers 4 16k;
    gzip_http_version 1.0;
    gzip_comp_level 2;
    gzip_types text/plain application/x-javascript text/css application/xml;
    gzip_vary on;
    gzip_disable "MSIE [1-6]\.";

可以用curl命令测试下是否设置生效

curl -I guoh.org/lifelog/ -H Accept-Encoding:gzip,defalte // 指定Head接收编码信息
curl -A "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)" -I guoh.org/lifelog/ -H Accept-Encoding:gzip,defalte // 指定代理为某种浏览器,可以用任意值来"欺骗"服务器
curl -A "Mozilla/4.73 [en] (X11; U; Linux 2.2.15 i686)" -I guoh.org/lifelog/ -H Accept-Encoding:gzip,defalte

配置静态文件超时时间

    location ~* .(ico|gif|bmp|jpg|jpeg|png|swf|js|css|mp3) {
        expires 30d;
    }

Nginx Proxy Cache如果有需要的话,再来研究。
http段里面添加

proxy_connect_timeout 10;
proxy_read_timeout 180;
proxy_send_timeout 5;
proxy_buffer_size 16k;
proxy_buffers 4 64k;
proxy_busy_buffers_size 256k;
proxy_temp_file_write_size 256k;
proxy_temp_path /tmp/temp_dir;
proxy_cache_path /tmp/cache levels=1:2 keys_zone=cache_one:100m inactive=1d max_size=10g;

server段里面添加

location ~ .*\.(gif|jpg|png|css|js)(.*) {
     proxy_pass http://appserver ;
     proxy_redirect off;
     proxy_set_header Host $host;
     proxy_cache cache_one;
     proxy_cache_valid 200 302 24h;
     proxy_cache_valid 301 30d;
     proxy_cache_valid any 5m;
     expires 90d;
}

WP插件有关
顺手把SyntaxHighlighter做了些调整,选用第三版,调整了下它的隔行变色以及高亮行颜色
vim lifelog/wp-content/plugins/syntaxhighlighter/syntaxhighlighter3/styles/shThemeDefault.css

.syntaxhighlighter .line.alt1 {
  background-color: white !important;
}
.syntaxhighlighter .line.alt2 {
  background-color: #EEE !important;
}
.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
  background-color: #FFFF00 !important;
}
Posted in Misc | Leave a comment

汉诺塔游戏

前天在一次面试中,面试官问到我汉诺塔(Towers of Hanoi)的问题,当时只知道是递归,知道手动玩游戏怎么玩,写程序还真的忘记了。大学我记得是有写过这样的作业,还有八皇后等等,不过真的都还给老师了,哈哈哈!

然后回家看了下,

http://www.cs.cmu.edu/~cburch/survey/recurse/hanoiimpl.html

http://www.python-course.eu/towers_of_hanoi.php

汉诺塔的介绍参见http://en.wikipedia.org/wiki/Tower_of_Hanoi等等

程序如下,

#include <stdio.h>

void move(char src, char dest)
{
	printf("\nMove the top plate of %c to %c", src, dest);
}

void hanoi(int n, char a, char b, char c)
{
	if (n == 1) { // 递归结束
		move(a, c); // 最小的那个盘子
	} else {
		hanoi(n - 1, a, c, b); // step 1,把所有小于最大的盘子移动到临时的柱子上(可以借助目的柱子)
		move(a, c); // step 2,把最大的盘子移动到目的柱子上
		hanoi(n - 1, b, a, c); // step 3,把临时的柱子上的盘子移动到目的柱子上(可以借助原始柱子),和step 1的方式一模一样

							// step 1和3都会有递归产生
	}
}

int main()
{
	int n;
	printf("\nPlz input number of the plates:");
	scanf("%d", &n);
	printf("\nMoving %d plates from A to C:", n);

	hanoi(n, 'A', 'B', 'C');

	printf("\nDone!\n");
}

如果你能看懂上面的注释,很好。如果你看不懂,也没关系,现在我们来试着详细点解释这个问题。
我们先来玩这个游戏,如图(点击图片看正常大图),
Towers of Hanoi
你会发现盘子的数量和最终要花费的步数是有关系的,为2^n - 1,也就是说只有1个盘子的时候需要1步,2个盘子的时候需要3步,3个盘子的时候需要7步,以此直到天荒地老。。。

所以这种思路我们是不是可以采用递归那完成呢?
比如3个盘子的时候,我们先移动上面的2个盘子(需要3步),再移动最大的1个盘子(需要1步),然后再移动那2个盘子到最大的盘子上面(需要3步),加起来一共就是7步,刚刚好。
并且2个盘子也可以同理拆分,最终会变成1个盘子的情况,1个盘子的情况很好解决,就1步,别且这也就是边界情况。

看起来这样是可以,再回头来看看程序,
step 1 ~ 3是不是很清晰了,并且step 1和3自己又会有递归产生,继续拆分,直到最后都是1个盘子的时候,递归结束。

如果不关心内部细节是不是就这样完美结束了呢?确实,如果你不关心内部细节,这样已经让人很容易的理解这个问题。但是总归会有一部分人会继续深挖。
现在我们以3个盘子为例,以程序执行的角度来看,

hanoi(3, 'A', 'B', 'C')
{
	hanoi(2, 'A', 'C', 'B');
		hanoi(1, 'A', 'B', 'C')
			move('A', 'C')
		move('A', 'B')
		hanoi(1, 'C', 'A', 'B')
			move('C', 'B')
	move('A', 'C');
	hanoi(2, 'B', 'A', 'C');
		hanoi(1, 'B', 'C', 'A')
			move('B', 'A')
		move('B', 'C')
		hanoi(1, 'A', 'B', 'C')
			move('A', 'C')
}

好像也没什么好写的,有机会看看非递归的方式来实现。

P.S.
递归有风险,使用需谨慎!

Posted in C, Paste | Leave a comment

C/CPP中的坑

记录C/CPP当中一些隐晦的问题,并非著名的C Traps and Pitfalls,虽然有可能包含一些相同的点。

1、类型退化(隐式转换)问题

#include <stdio.h>

#define SIZEOF(A) \
	sizeof(A) / sizeof(A[0])

inline static int sizeOfCharArray(char a[])
{
	return sizeof(a) / sizeof(a[0]); // type casting by default
}

int main(int argc, char **argv)
{
	char a[10];
	a[0] = 5;
	a[9] = 8;

	void *p = &a; // do it explicitly

	printf("sizeof char array %d %lu %lu\n", sizeOfCharArray(a), sizeof((char*) p), SIZEOF(a));

	return 0;
}

注意这段程序当中sizeOfCharArray方法,其实在很多情况都会有隐式转换,但是和sizeof一起用的时候要注意。

2、多层循环,效率问题

http://rednaxelafx.iteye.com/blog/352730

Posted in C, C++, Paste | Leave a comment

H.264预测之帧间预测

这是一篇阅读笔记,直接点击图片可以查看清晰大图。

参考资料
The H.264 Advanced Video Compression Standard, Second Edition,以下简称THAVCS
Information technology - Coding of audio-visual objects - Part 10: Advanced Video Coding,以下简称SPEC

CodecVisa
JM
foreman_part_qcif.264 这个是foreman_part_qcif.yuv通过JM 8.6转换来的

http://blog.csdn.net/stpeace/article/details/8115392

帧间预测就是以已经编码好的帧(在display order上可以是当前帧的前面也可以是后面)作为参考帧,确定预测区域,生成预测块,计算出殘差,这些参考帧都存放于Decoded Picture Buffer当中。与帧内预测不同的是,帧间预测是以重建的帧为预测帧的,而帧内预测是一Loop filter之前的帧为预测帧。当前区块和预测区域之间的位移为运动向量(Motion Vector,简写为MV),每个区块有各自的MV,并且MV可以是整数精度,二分之一精度或者四分之一精度(对于4:2:0的视频,C是八分之一精度),这种非整数精度的预测区域都是通过插值算法从参考帧当中计算出来的。

注意,在实际当中MV的单位都是以最小的精度为单位的,比如Y分量的MV单位就是四分之一精度,C分量的MV单位就是八分之一精度,当然这里说的都是4:2:0的视频。

主要过程就是选取参考帧,插值,确定预测区块,确定预测类型,确定运动向量,预测运动向量,编码运动向量差量和殘差,deblocking filter。。。
这里插值计算需要注意的是,先计算二分之一,再计算四分之一,参见THAVCS 6.4.2.1 Generating interpolated sub-pixel samples。

我们来看个实例,先看MV值为整数的,也就是不用插值的。

跟帧内预测所用的码流一样,用CodecVisa打开,选取第二帧(这个码流一共三帧,分别为IPP),选取第4行,第9列的那个MB。
看图inter-prediction-p-slice,
inter-prediction-p-slice
因为这里Y分量MV单位是四分之一精度,所以实际值除以4就是像素偏差。被高亮的块的值向左移动7个像素就刚刚和第一帧当中被高亮位置的值相等,这也就是说当前块是以第一帧所示区块为预测块的,注意帧间预测的参考帧都是重构出来的,都是看Final值,和帧内预测看Pre-LP值不一样,如图inter-prediction-reference-picture,
inter-prediction-reference-picture
这是帧间预测的一个实例。

再来看看非整数精度的情况,MV为(-2.75, -2),垂直方向上是整数,我们不用考虑,现在就看水平方向上。
inter-prediction-p-slice-non-integer-pixel
inter-prediction-reference-picture-non-integer-pixel
如上两张图就分别是当前块和预测块,因为这个MV不是整数的,所以要先插值算出预测块,我们把有需要的数据提取出来,

187    185    187    195 A   199    201    200    200

187    184    191    198 B   199    201    199    200

188    183    174    169 C   183    202    198    200

189    185    166    130 D   132    172    199    202

如上数据就是参考帧重构后的数据,A,B,C和D就是我们要插值算出来的数据,也就是当前块的预测值。

先计算二分之一

Aa = round((1 * 185 - 5 * 187 + 20 * 195 + 20 * 199 - 5 * 201 + 1 * 200) / 32) = 198
Bb = round((1 * 184 - 5 * 191 + 20 * 198 + 20 * 199 - 5 * 201 + 1 * 199) / 32) = 199
Cc = round((1 * 183 - 5 * 174 + 20 * 169 + 20 * 183 - 5 * 202 + 1 * 198) / 32) = 173
Dd = round((1 * 185 - 5 * 166 + 20 * 130 + 20 * 132 - 5 * 172 + 1 * 199) / 32) = 123

然后四分之一

A = round((198 + 195) / 2) = 197
B = round((199 + 198) / 2) = 199
C = round((173 + 169) / 2) = 171
D = round((123 + 130) / 2) = 127

第二列的数据,方法同样
先计算二分之一

Aa + 1 = round((1 * 187 - 5 * 195 + 20 * 199 + 20 * 201 - 5 * 200 + 1 * 200) / 32) = 200
Bb + 1 = round((1 * 191 - 5 * 198 + 20 * 199 + 20 * 201 - 5 * 199 + 1 * 200) / 32) = 200
Cc + 1 = round((1 * 174 - 5 * 169 + 20 * 183 + 20 * 202 - 5 * 198 + 1 * 200) / 32) = 195
Dd + 1 = round((1 * 166 - 5 * 130 + 20 * 132 + 20 * 172 - 5 * 199 + 1 * 202) / 32) = 150

然后四分之一

A = round((200 + 199) / 2) = 200
B = round((200 + 199) / 2) = 200
C = round((195 + 183) / 2) = 189
D = round((150 + 132) / 2) = 141

后面的就不再罗列了,从结果来看,我们这里预测的结果

197    200    ....
199    200    ....
171    189    ....
127    141    ....

和CodecVisa有些许出入,但是变化趋势相同的,所以这个实验的基本目的达到了。

Posted in Multimedia, Paste | Leave a comment