2013年2月27日 星期三

[Apache] Web server 強制使用 https 連線 @ Ubuntu 12.04

最近在架服務時,突然蹦出個強制都走 https 的念頭,找了一下果然可以用 rewrite rules 來解決 :P


$ sudo a2ensite default-ssl
$ sudo a2enmode rewrite ssl
$ sudo vim default
DocumentRoot /var/www
<Directory />
  Options FollowSymLinks
  AllowOverride None


  RewriteEngine on
  RewriteCond %{SERVER_PORT} !^443$
  RewriteRule ^.*$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R]
</Directory>
<Directory /var/www/>
  Options Indexes FollowSymLinks MultiViews
  AllowOverride None
  Order allow,deny
  allow from all


  RewriteEngine on
  RewriteCond %{SERVER_PORT} !^443$
  RewriteRule ^.*$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R]
</Directory>


2013年2月26日 星期二

[DD-WRT] Port forwarding via LAN is not working

ddwrt


最近周邊的 AP 不知不覺都刷成 DD-WRT 版本 :P 好處是設定方式越來越習慣,不用換台 AP 就要練習新的設定方式。但最近碰到一個現象,那就是設定好的 Port forwarding ,竟然在 LAN 裡頭無法使用,但在無線網路中是正常運作的。找了會,終於發現問題:r15760 breaks NAT loopback


解法就是到 Administration > Commands :


insmod ipt_mark
insmod xt_mark
iptables -t mangle -A PREROUTING -i ! `get_wanface` -d `nvram get wan_ipaddr` -j MARK --set-mark 0xd001
iptables -t nat -A POSTROUTING -m mark --mark 0xd001 -j MASQUERADE

接著按 Run commands 跟 Save Firewall 即可 :P


2013年2月24日 星期日

[VIM] using clang_complete plugin @ Ubuntu 12.04

clang_complete


最近開始狂寫 CPP 和使用 boost ,寫 CPP 對我而言最大的問題是找 library 的用法。回想起寫 php 時,總是憑著 C function 印象,丟著 keyword 到網路上就能找到,這時面對 CPP 跟 boost 時,因為沒有相關的 background 就卡關了 :P 所幸高手同事推薦了 clang_complete 給我把玩!對 CPP 甚至 boost 的操作終於可以輕鬆一點啦!


至於安裝 clang_complete 的方式,主要是要去下載 https://github.com/Rip-Rip/clang_complete 囉:


$ sudo apt-get install clang libclang-dev
$ git clone git://github.com/Rip-Rip/clang_complete.git
$ cd clang_complete
$ make install


試試吧:


$ vim test.cpp 


若有找不到的地方,那就當前目錄多加 .clang_complete 檔案,內容則描述要去哪邊找,如:


$ cat .clang_complete
-I/usr/include/c++/4.6.3/


2013年2月17日 星期日

Android 開發筆記 - 網路廣播程式及實作原理 (使用FFmpeg播放mms)


因為自己的手機沒廣播裝置無法直接接收無線電波來播放,所以一直很想寫網路廣播軟體程式,直到去年秋天找了一下如何在 Android 播放 mms 串流後,發現本身 Android 還是不太支援,就開始嘗試編 ffmpeg 處理 mms 播放,而編出 ffmpeg 後又停擺一陣子,實在是隔行如隔山 :P 所幸春節還有一點時間,卡在心中好一陣子的議題,終於趁著春節最後一天把該弄懂得都弄的差不多了,順手記一下,等下一個空閒時刻再包成一個完整一點的 android app 吧!


參考資料:



簡言之,如果想寫隻程式播放網路廣播(mms)的流程,首先要確認 Android 本身是否已支援播放 mms protocol:


try {
    MediaPlayer mMediaPlayer = MediaPlayer.create(this, Uri.parse("http://mms.example.com/radio/"));
    mMediaPlayer.prepare();
    mMediaPlayer.start();
} catch (Exception e) {
    e.printStackTrace();
}


若要測試指定的 mms 位置是否正確,可以先用 ffplay (安裝ffmpeg) 測試,(記得要用 mmsh:// ):


$ ffplay mmsh://mms.example.com/radio/


最後,則是寫程式用 ffmepg 相關程式碼來寫隻簡單的 Android app,其處理流程:



  1. 在 Java 端呼叫 NDK 進行 mms decoding

  2. 在 NDK 中,使用 ffmepg library 對 mms 來源進行 audio decoding

  3. 在 NDK 中,請 Java 層初始化 AudioTrack 物件,並記得 mAudioTrack.play()

  4. 在 NDK 中,定期將 decoding 資料餵給 Java 層 AudioTrack


記得要給 <uses-permission android:name="android.permission.INTERNET"/> 權限!測試的實機也要開網路,不然會得到 I/O Error 的訊息。


MainActivity.java:


public class MainActivity extends Activity {


   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      playRadioNDKInit();
      playRadioNDKRun("mmsh://xxx.xxx.xxx.xxx/");

      /*
      try {
         MediaPlayer mMediaPlayer = MediaPlayer.create(this, Uri.parse("http://xxx.xxx.xxx.xxx/"));
         mMediaPlayer.prepare();
         mMediaPlayer.start();
      } catch (Exception e) {
         e.printStackTrace();
      }
      // */
   }


   AudioTrack playRadio_AudioTrack = null;
   int playRadio_MinBufferSize = 0;
   int playRadio_BytesWritten = 0;
   List<byte []> playRadio_AudioBuffer = new ArrayList<byte []>();

   void playRadio_Step1_Init(int AVCodecContext_sampleRate, int AVCodecContext_channels) {
      android.util.Log.e("MainActivity", "playRadio_Step1_Init:"+AVCodecContext_sampleRate+","+AVCodecContext_channels);
      AVCodecContext_channels = (AVCodecContext_channels == 1) ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO;
      playRadio_MinBufferSize = AudioTrack.getMinBufferSize(AVCodecContext_sampleRate, AVCodecContext_channels, AudioFormat.ENCODING_PCM_16BIT) * 4;
      playRadio_BytesWritten = 0;
      playRadio_AudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, AVCodecContext_sampleRate, AVCodecContext_channels, AudioFormat.ENCODING_PCM_16BIT, playRadio_MinBufferSize, AudioTrack.MODE_STREAM);
   }

   int playRadio_Step2_fillBuffer(byte[] audio_frame_buffer) {
      if(audio_frame_buffer.length > 0 ) {
         playRadio_AudioBuffer.add(playRadio_AudioBuffer.size(), audio_frame_buffer);
         playRadio_BytesWritten += audio_frame_buffer.length;
         if(playRadio_BytesWritten > playRadio_MinBufferSize)
            return 0;
      }
      return 1;
   }

   void playRadio_Step3_audioTrackWrite(byte[] audio_frame_buffer) {
      if( playRadio_AudioTrack != null) {
         if( playRadio_AudioBuffer.size() > 0 ) {
            for(int i=0; i<playRadio_AudioBuffer.size(); ++i) {
               byte []audoFrameBuffer = playRadio_AudioBuffer.get(i);
               playRadio_AudioTrack.write(audoFrameBuffer, 0, audoFrameBuffer.length);
            }
            playRadio_AudioBuffer.clear();
            playRadio_BytesWritten = 0;
         }
         if( audio_frame_buffer.length > 0 )
            playRadio_AudioTrack.write(audio_frame_buffer, 0, audio_frame_buffer.length);
         playRadio_AudioTrack.play();
      }
   }
   public native void playRadioNDKInit();
   public native void playRadioNDKRun(String mmsh_uri);
   static {
      System.loadLibrary("ffmpeg");
      System.loadLibrary("ffmpeg-jni");
   }
}


jni/ffmpeg-ini.c:


#include <string.h>
#include <jni.h>
#include <android/log.h>
#include <stdio.h>


#include "libavformat/avformat.h"


#define LOG_TAG "FFmpegJNI"
#define LOGI(...) {__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__);}
#define LOGE(...) {__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__);}


jstring Java_com_example_internetradioplayer_MainActivity_stringFromJNI( JNIEnv* env, jobject thiz )
{
   return (*env)->NewStringUTF(env, "Hello from My JNI !");
}


static jmethodID playRadio_Step1_Init;
static jmethodID playRadio_Step2_fillBuffer;
static jmethodID playRadio_Step3_audioTrackWrite;


void Java_com_example_internetradioplayer_MainActivity_playRadioNDKInit( JNIEnv* env, jobject thiz )
{
   jclass cls = (*env)->GetObjectClass(env, thiz);


   playRadio_Step1_Init = (*env)->GetMethodID(env, cls, "playRadio_Step1_Init", "(II)V");
   if(!playRadio_Step1_Init)
      LOGE("playRadio_Step1_Init not found");
   playRadio_Step2_fillBuffer = (*env)->GetMethodID(env, cls, "playRadio_Step2_fillBuffer", "([B)I");
   if(!playRadio_Step2_fillBuffer)
      LOGE("playRadio_Step2_fillBuffer not found");
   playRadio_Step3_audioTrackWrite = (*env)->GetMethodID(env, cls, "playRadio_Step3_audioTrackWrite", "([B)V");
   if(!playRadio_Step3_audioTrackWrite)
      LOGE("playRadio_Step3_audioTrackWrite not found");
}


void Java_com_example_internetradioplayer_MainActivity_playRadioNDKRun( JNIEnv* env, jobject thiz, jstring mms_uri )
{
   int stream_index = -1, i = 0, ret = 0;
   AVCodec *codec;
   AVFormatContext *pFormatCtx = avformat_alloc_context();
   avcodec_register_all();
   av_register_all();
   avformat_network_init();


   // jstring to char *
   const int out_buffer_max_line = 1024;
   char mms_location[out_buffer_max_line+1];
   jbyteArray java_bytes_array= (jbyteArray)(*env)->CallObjectMethod(env, mms_uri, (*env)->GetMethodID(env, (*env)->FindClass(env, "java/lang/String"), "getBytes", "(Ljava/lang/String;)[B"), (*env)->NewStringUTF(env, "utf-8"));
   jsize java_bytes_array_length = (*env)->GetArrayLength(env, java_bytes_array);
   jbyte* java_bytes = (*env)->GetByteArrayElements(env, java_bytes_array, JNI_FALSE);
   if(java_bytes_array_length > 0 && java_bytes_array_length < out_buffer_max_line ) {
      memcpy(mms_location, java_bytes, java_bytes_array_length);
      mms_location[java_bytes_array_length] = '\0';
   } else {
      mms_location[0] = '\0';
   }


   if ( ( ret = avformat_open_input(&pFormatCtx, mms_location, NULL, NULL) ) != 0) {
      LOGE("avformat_open_input() failed: (%d, %s)", ret, av_err2str(ret));
      return;
   }


   if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
      LOGE("Unable to locate stream information");
      return;
   }


   // Find the first audio stream
   for (i = 0; i < pFormatCtx->nb_streams; i++) {
      if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
         stream_index = i;
         break;
      }
   }


   if (stream_index == -1) {
      LOGE("stream_index == -1");
      return;
   }


   if( (codec = avcodec_find_decoder(pFormatCtx->streams[stream_index]->codec->codec_id) ) == NULL ) {
      LOGE("avcodec_find_decoder() failed to find audio decoder");
      return;
   }


   if (avcodec_open2(pFormatCtx->streams[stream_index]->codec, codec, NULL) < 0) {
      LOGE("avcodec_open2() failed");
      return;
   }


   (*env)->CallVoidMethod(env, thiz, playRadio_Step1_Init, pFormatCtx->streams[stream_index]->codec->sample_rate, pFormatCtx->streams[stream_index]->codec->channels);


   AVPacket packet;
   memset(&packet, 0, sizeof(packet));
   av_init_packet(&packet);


   AVFrame *decoded_frame = avcodec_alloc_frame();
   int got_frame;
   jbyteArray retArray = NULL;


   int flag_continue = 1;
   while(flag_continue) {
      ret = av_read_frame(pFormatCtx, &packet);
      if( ret < 0 ) {
         if (ret == AVERROR_EOF || url_feof(pFormatCtx->pb)) {
            LOGE("buffering, break", pFormatCtx->nb_streams);
            break;
         }
      }
      if( packet.stream_index != stream_index )
         continue;
      if( ( ret = avcodec_decode_audio4(pFormatCtx->streams[stream_index]->codec, decoded_frame, &got_frame, &packet) ) <= 0 && got_frame ) {
         LOGE("buffering, no frame: %d, %d", ret, got_frame);
      } else {
         int out_size = av_samples_get_buffer_size(NULL, pFormatCtx->streams[stream_index]->codec->channels, decoded_frame->nb_samples, pFormatCtx->streams[stream_index]->codec->sample_fmt, 1);
         LOGE("buffering, avcodec_decode_audio4 %d , %d, %d, %d", packet.stream_index, stream_index, ret, out_size);


         if(!retArray)
            retArray = (*env)->NewByteArray(env, out_size);
         else if((*env)->GetArrayLength(env, retArray) != out_size) {
            (*env)->DeleteLocalRef(env, retArray);
            retArray = (*env)->NewByteArray(env, out_size);
         }
         void *temp = (*env)->GetPrimitiveArrayCritical(env, (jarray)retArray, 0);
         memcpy(temp, decoded_frame->data[0], out_size);
         (*env)->ReleasePrimitiveArrayCritical(env, retArray, temp, 0);
         flag_continue = (*env)->CallIntMethod(env, thiz, playRadio_Step2_fillBuffer, retArray);
      }
      av_free_packet(&packet);
   }


   while (av_read_frame(pFormatCtx, &packet) >= 0){
      if( ( ret = avcodec_decode_audio4(pFormatCtx->streams[stream_index]->codec, decoded_frame, &got_frame, &packet) ) <= 0 && got_frame ) {
         LOGE("no frame");
         break;
      } else {
         int out_size = av_samples_get_buffer_size(NULL, pFormatCtx->streams[stream_index]->codec->channels, decoded_frame->nb_samples, pFormatCtx->streams[stream_index]->codec->sample_fmt, 1);


         if(!retArray)
            retArray = (*env)->NewByteArray(env, out_size);
         else if((*env)->GetArrayLength(env, retArray) != out_size) {
            (*env)->DeleteLocalRef(env, retArray);
            retArray = (*env)->NewByteArray(env, out_size);
         }
         void *temp = (*env)->GetPrimitiveArrayCritical(env, (jarray)retArray, 0);
         memcpy(temp, decoded_frame->data[0], out_size);
         (*env)->ReleasePrimitiveArrayCritical(env, retArray, temp, 0);
         (*env)->CallVoidMethod(env, thiz, playRadio_Step3_audioTrackWrite, retArray);
      }
   }
   if(retArray)
      (*env)->DeleteLocalRef(env, retArray);
}


jni/Android.mk:


LOCAL_PATH := $(call my-dir)


include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeg-prebuilt
LOCAL_SRC_FILES := ffmpeg/armv7/libffmpeg.so
LOCAL_EXPORT_C_INCLUDES := ffmpeg/armv7/include
LOCAL_EXPORT_LDLIBS := ffmpeg/armv7/libffmpeg.so
LOCAL_PRELINK_MODULE := true
include $(PREBUILT_SHARED_LIBRARY)


include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeg-jni
LOCAL_SRC_FILES := ffmpeg-jni.c
LOCAL_C_INCLUDES := $(LOCAL_PATH)/ffmpeg/armv7/include
LOCAL_LDLIBS := $(LOCAL_PATH)/ffmpeg/armv7/libffmpeg.so -lm -llog
LOCAL_SHARED_LIBRARY := ffmpeg-prebuilt
include $(BUILD_SHARED_LIBRARY)


以上程式在 Nexus S/Android 4.1.2 實機測試可以播出聲音,且在 Android emulator 4.1 & ARM (armeabi-v7a) 亦可以播出聲音,代表 ffmpeg 的使用還算正常,但此篇僅單純記錄如何 call ffmpeg functions,若要真的弄出可以跑在各種平台的程式且正確使用,還須需要:



  • 調整編譯 ffmpeg 以及 jni/Android.mk 的編譯方式以支援各種 CPU 平台

  • 實作 threading、event callback 等細節,此例仍是卡在 main thread 上

  • 實作播放系列,例如暫停、開始、停掉等功能


2013年2月13日 星期三

Android 開發筆記 - 使用第三方函式庫流程 (以FFmpeg為例)

 + 


雖然 Android framework 已經有不錯的函式庫,但總是會有不足之處。這時就先來玩玩 FFmpeg 吧,首先要做的事就是編譯出可以在 Android 執行的函式庫,網路上不少編譯 FFmpeg for Android(ARM) 的文章,最後我就在 Ubuntu 12.04 64Bit 上試編一下,並在 Mac OS 10.8 上把玩 Eclipse 囉


相關資源:



首先,就在 Ubuntu 下載好 Android NDK 後,安裝一些基本環境:


$ sudo apt-get install ia32-libs git make


接著取出 github.com/vecio/FFmpeg-Android 後,直接修改 FFmpeg-Android.sh:


- cd ffmpeg && patch -p1 <../FFmpeg-VPlayer.patch
+ cd ffmpeg && git checkout 5e99df019a850e9ffa96d73e72b8a47a93a61de8
+ #cd ffmpeg && patch -p1 <../FFmpeg-VPlayer.patch


-export CC="ccache arm-linux-androideabi-gcc"
+#export CC="ccache arm-linux-androideabi-gcc"
+export CC=arm-linux-androideabi-gcc


-for version in neon armv7 vfp armv6; do
+#for version in neon armv7 vfp armv6; do
+for version in armv7 armv6; do


- [ $PIPESTATUS == 0 ] || exit 1
+ #[ $PIPESTATUS == 0 ] || exit 1


接著設定好 ANDROID_NDK 環境變數後,就可以開始編譯了,在我的 VM 中,大概跑了好一陣子,約 20 分鐘:


$ export ANDROID_NDK=~/android-ndk-r8d
$ cd ~/FFmpeg-Android
$ sh FFmpeg-Android.sh
$ ls ~/FFmpeg-Android/build/ffmpeg/
armv6  armv7


在把 ~/FFmpeg-Android/build/ffmpeg/ 複製到 Mac OS X 使用。而在 Mac OS X 10.8 上,就單純用 Eclipse 跟 Androd NDK 相關的設置,建立一個 Android Project 出來,此例為 HelloNDKWithFFmpeg ,使用 Android 4.0 SDK,並新增一個 jni 目錄,裡頭擺上之前編好的 ffmpeg 項目,以 Android.mk 和 ffmpeg-jni.c (其他如自動編譯的設定請參考 Android 開發筆記 - 使用 Android NDK ):


android_ffmpeg_eclipse


Android.mk:


LOCAL_PATH := $(call my-dir)


include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeg-prebuilt
LOCAL_SRC_FILES := ffmpeg/armv7/libffmpeg.so
LOCAL_EXPORT_C_INCLUDES := ffmpeg/armv7/include
LOCAL_EXPORT_LDLIBS := ffmpeg/armv7/libffmpeg.so
LOCAL_PRELINK_MODULE := true
include $(PREBUILT_SHARED_LIBRARY)


include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeg-jni
LOCAL_SRC_FILES := ffmpeg-jni.c
LOCAL_C_INCLUDES := $(LOCAL_PATH)/ffmpeg/armv7/include
LOCAL_LDLIBS := -lm -llog $(LOCAL_PATH)/ffmpeg/armv7/libffmpeg.so
#LOCAL_SHARED_LIBRARY := ffmpeg-prebuilt

include $(BUILD_SHARED_LIBRARY)


ffmpeg-jni.c:


#include <string.h>
#include <jni.h>
#include <android/log.h>


#include "libavformat/avformat.h"


#define LOG_TAG "FFmpegJNI"
#define LOGI(...) {__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__);}
#define LOGE(...) {__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__);}


jstring Java_com_example_hellondkwithffmpeg_MainActivity_stringFromJNI( JNIEnv* env, jobject thiz )
{
    return (*env)->NewStringUTF(env, "Hello from My JNI !");
}


void Java_com_example_hellondkwithffmpeg_MainActivity_getVersions(JNIEnv *env, jclass jc, jobject jvers)
{
    int *vers = (*env)->GetDirectBufferAddress(env, jvers);
    vers[0] = LIBAVFORMAT_VERSION_MAJOR;
    vers[1] = LIBAVCODEC_VERSION_MAJOR;
    vers[2] = LIBAVUTIL_VERSION_MAJOR;
}


MainActivity.java:


package com.example.hellondkwithffmpeg;


import java.nio.ByteBuffer;
import java.nio.ByteOrder;


import android.os.Bundle;
import android.app.Activity;
import android.widget.TextView;


public class MainActivity extends Activity {


   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      //setContentView(R.layout.activity_main);
      TextView tv = new TextView(this);
      //tv.setText( stringFromJNI() );
      tv.setText( getVersions() );
      setContentView(tv);
   }


   String getVersions() {
      ByteBuffer bvers = ByteBuffer.allocateDirect(4 * 3).order(ByteOrder.nativeOrder());
      getVersions(bvers);
      int[] vers = new int[3];
      bvers.asIntBuffer().get(vers);
      return "LIBAVFORMAT_VERSION_MAJOR: "+vers[2]+"\nLIBAVCODEC_VERSION_MAJOR: "+vers[1]+"\nLIBAVUTIL_VERSION_MAJOR: "+vers[0];
   }

   public native String stringFromJNI();
   public native void getVersions(ByteBuffer b);
   static {
      System.loadLibrary("ffmpeg");
      System.loadLibrary("ffmpeg-jni");
   }
}


成果(Nexus S實機):


android_ffmepg


整體上的使用心得是在如何編譯出第三方函式庫以及 Android.mk 的設定,例如該去哪編找 header files 等,其他跟一般 Android NDK 教學文沒啥兩樣囉。


2013年2月7日 星期四

[ARM] 查看所需的 shared library @ Ubuntu 12.04

以前常用 ldd 來看一支程式到底吃了哪些 shared libraries ,然而在板子上卻不知該如何下手 :P 後來終於找到關鍵字 readelf 啦!所以就先找一下 cross compiler tools:


$ find /path -name "*readelf*"
/path/bin/armv6z-*-linux-gnueabi-readelf


接著用這工具就行:


$ /path/bin/armv6z-*-linux-gnueabi-readelf -a my_arm_tool | grep "Shared"
0x00000001 (NEEDED) Shared library: [libjsqlite.so]
0x00000001 (NEEDED) Shared library: [libjson.so]
0x00000001 (NEEDED) Shared library: [libc.so.6]
...


2013年2月6日 星期三

Ubuntu Desktop Reset @ Ubuntu 12.04

Ubuntu 12.04 Desktop 預設的桌面是 tty7,可以用 ps -e | grep tty 看到。最近有台機器很久沒去看,發現桌面又掛了,除了滑鼠可以動之外,其他都動不了。常見的解法是重開機,可惜那台又是當 server 跑服務,最後遠端登入後就是送 kill 訊號給 tty7 了(此例恰好console端鍵盤無效,連切tty都不行)。


指令:


$ sudo kill `ps -e | grep tty7 | awk '{print $1}'`


2013年2月4日 星期一

使用 ffmpeg 製作手機鈴聲 @ Ubuntu 12.04

雖然好像有不少方便的線上轉檔,但我還是很龜毛的自己動動手

工具:
  • 用 ffmpeg 將影片轉成(抽出音樂) mp3


    • $ ffmpeg -i input.mp4 out.mp3

    • $ ffmpeg -i input.flv out.mp3

    • $ ffmpeg -itsoffset 00:00:00.000 -i input.mp4 -t 00:00:51.000 output.mp3


      • -itsoffset 00:00:00 代表從開頭開始

      • -t 00:00:51.00 代表取 51 秒

VS2012 編譯給 Windows XP 使用 @ Windows 7

VS2012_exe


最近用 VS2012 編譯程式時,發現預設的狀況是不給 Windows XP 用的,據說當時推出 VS2012 時有意拋棄掉 XP 的,總之,後來有推出修正方式了。


下載 vsupdate_KB2707250.exe 更新檔後,接著再編譯專案時,記得選擇 Visual Studio 2012 - Windows XP (v110_xp) 即可。


VS2012_xp_exe


此外,若想要成 static 的,別忘了勾選 /MT 系列。


VS2012_without_NR


2013年2月3日 星期日

目標?

清交小徑


週末快閃新竹,跟老同學聊聊天,在網路上又跟學弟喇嘞幾句,聊聊近況,實在愜意。


最近的變化說大不大,說小不小,在工作上開始帶 team ,說來很妙,我自己一直沒有想要組 team 的生活,頂多想找一兩位同好把玩,也隨時準備單打獨鬥,並不是排斥組 team ,實在是人一旦便多後,管理上就要多花上許多時間 :P 若花了心思處理,就等同自己少了時間把玩東西。上週一突然多了個 dealine 後,很慶幸目前的成員都進行的很積極,反而漸漸看到一個 team 的價值,真的很讚。


至於人生的目標呢?去年碰到健康問題,說真的頓時目標就縮到很簡單:及時行樂。週末不會想窩在電腦前面,就算窩在電腦前面也不會想 coding 了,看看 PPS 給的清單也挺滿意的。此外,隨時都想帶著手機或相機去外頭拍個幾張,昨天去趟新竹後,對於照片的感觸又更深刻了,不再是追求多亮眼的場景,反而滲一點記憶足跡,挺滿足了。


至於同輩之間的目標呢?工作三四年後,不少人開始追求年薪了。雖然 22k 議題常提,但在資訊界中要找間年薪100的仍不是多大的問題 :P 有家庭的,繼續加碼到 150,有的在賺錢公司的則繼續追趕到200不等。至於自己嘛 @_@ 就像當年離開新竹時的心境,臭屁地認為既然都是成功,那先繞路走走也不錯吧 :) 妙的是,我發現周邊人換工作的主因,不見得是對未來有新的期待,而只是單純對環境膩了,薪資絕對不是最後一根稻草。


錢錢這事永遠是不嫌多的,我仍記著前室友跟我分享十多年的工作心得:


月薪從3萬多變到4萬多時,你會很有感覺,但月薪從5萬多,變到7萬多、8萬多、9萬多時,漸漸地你會沒有感覺的


此刻嘛,再給一些任性享受青春的熱血吧 :)


啊,又想旅行了。


2013年2月2日 星期六

[C++] JSON Spirit Unicode Usage @ Mac OS X 10.8.2

最近接手同事用 C++ 寫的程式,過程中看到使用 JSON Spirit 來處理 JSON 格式,然而,卻發現字串處理有些問題,追了一下才發現這是函式庫沒有使用好而已。


簡單的說,如果要處理 Unicode 的部份,請改用 w開頭系列,如 json_spirit::wmValue 等。


簡易範例:


#include <iostream>
#include <string>
#include <json_spirit/json_spirit.h>
#define JSON_MESSAGE_STRING "{\"message\":\"\\u771f\\u5a01\"}"
#define JSON_MESSAGE_WSTRING L"{\"message\":\"\\u771f\\u5a01\"}"


int main() {
   std::string data = std::string(JSON_MESSAGE_STRING);
   std::cout << "Source: " << data << std::endl;


   json_spirit::mValue mValue;
   json_spirit::read(data, mValue);
   std::cout << "mValue: " << json_spirit::write(mValue) << std::endl;


   std::wstring wdata = std::wstring(JSON_MESSAGE_WSTRING);
   json_spirit::wmValue wmValue;
   json_spirit::read(wdata, wmValue);
   std::wcout << "wmValue:" << json_spirit::write(wmValue) << std::endl;

   return 0;
}


輸出:(在 Mac 上需安裝 Xcode 及其相關 command-tools 後,再用 MacPorts 安裝 boost 即可,接著下載 json_spirit 後,用 cmake 編出 libjson_spirit.a)


> g++ -I/opt/local/include -Ijson_spirit/ test.cpp libjson_spirit.a
> ./a.out
Source: {"message":"\u771f\u5a01"}
mValue: {"message":"\u001F\u0001"}
wmValue:{"message":"\u771F\u5A01"}


可以清楚看到,若不用 std::wstring 這類的處理,解碼就會出錯了。接下來的問題就縮到如何將 std::string 轉到 std::wstring 了 Orz


非常簡易的 std::string to std::wstring 硬轉法:


std::string data;
std::wstring wdata;
wdata.assign(data.begin(), data.end());


至於要輸出 wstring 的方式,繞路一陣子後,終於也找到解法:


std::wstring outwstr = wmValue.get_obj().find(L"message")->second.get_str();
std::cout << "UTF-8:" << boost::locale::conv::utf_to_utf<char>(outwstr) << std::endl;


2013年2月1日 星期五

輸出解碼後的 Unicode ("\u771f\u5a01") 字串 @ Ubuntu 12.04

今天驗證某套 JSON library 到底有沒解碼錯誤,一直再想該怎樣把 '\u' 這類開頭的字串解碼出來,後來就想到幾乎快隨處都可以用的 python 啦


print u"\u771f\u5a01"


如此就解碼完了,搭配 Ubuntu 環境預設是 UTF-8 編碼,在 Terminal 顯示 UTF-8 字串就幾乎無痛了。