1. Canon Raw Codec for Vista / XP x64 ...

    沒想到還真的有人在寫這東西...

    http://www.ardfry.com/cr2-codec/

     

    只不過這不是 CANON 官方出的,是有高手自己在寫,拿來賣錢用的... USD 29.95 元... 現在預購只要 USD 19.95...

    我怎麼幫它打起廣告來了... 其實真的應該給他鼓勵一下,不過 20 元也不是小數目,這篇就當贊助作者的廣告吧,哈哈...

    我自己? 之前找到的在 Vista x64 下,用 WOW 來跑的爛招就加減用... 我看看 CANON 啥時才會推出官方的 x64 版本 codec ... @_@

    2008/06/11 技術隨筆 有的沒的

  2. 換了四核心,MCE就掛了...

     

    這年頭,標題不下的聳動一點就沒人看了... 不過我生性保守,這標題可是一點也不誇張... 補一下前陣子狠下心買了顆 Q9300 來給 SERVER 用,發生的小障礙及處理過程。新的 CPU 裝在 SERVER 上, SERVER 關在機櫃裡看不到摸不到,當然是先裝到桌機享受一下,桌機用的是 Vista,平常會用用 MCE 看電視,還有錄電視下來慢慢看...

    把桌機的 E6300 換掉,換 Q9300 上去,不錯,什麼東西都很正常,速度也快,溫度也低...。正當一切都很美好的時後,想說開個 MCE 來看電視,嘖... 掛了? 出現這個美化過的 General Protection Failure 畫面:

     

    點了建議選項,無解。點了 DETAIL 下去看,也看不出個所以然...

     

    Microsoft 這次算是有良心,沒有再寫 "請聯絡您的系統管理員" ... 別再叫 USER 找系統管理員了,最好這種五四三的問題跟外星人的數字,系統管理員都看的懂...

     

    看了這些資料,也看不出問題在那,換了 Media Player 來播放之前 MCE 錄下來的 *.dvr-ms 也是出現一樣的錯誤,連 Media Player Classic 通通都一樣,害我懷疑起是不是 Microsoft 的 codec 寫的不好,在四核心CPU上會有什麼怪問題發生?

     

    不過越看越不對,隨便查了一下 GOOGLE,有一堆人用 Q9300 用 MCE 用的好好的啊... 回頭再來看看詳細的資訊,看到比較特別的,錯誤模組: Indiv01.key ? 這不像是 DLL 還是啥的,怎麼會錯在這? 就順手 GOOGLE 了一下...

    沒啥用,只知道這是 Microsoft 的 DRM 用的秘鑰,再去查 Microsoft 的 support center,這次像樣多了,找到這篇:

    KB891664: 如果您的電腦硬體經過變更,Windows Media Digital Rights Management 系統可能會無法運作

     

    真是它ㄨㄨㄨㄨㄨ的,找到問題了... 原來各種加秘的演算法都需要一個 KEY,來識別使用者,或是某一份發佈的媒體檔案。為了讓每一台 PC 有不同的 KEY,因此 Microsoft 會有自己的演算法來替每台 PC 產生一組 KEY,就是 Indiv01.key 這檔案的內容。產生 KEY 的過程中,硬體組態 (包含可用的 CPU 數量) 是來源的一部份,因此換了顆 CPU (核心個數有變化),就會被 Microsoft 視為是換了台電腦了...。

     

    就是這鳥問題,MCE 認為我換了台新電腦,又用舊的 KEY,判定是 "違法" 的複製 Media,就給我擋了下來... 照著這篇文章的步驟,可以清除 DRM 的授權,一切重頭來過,果然就正常了! BINGO。

     

    有點鳥的問題,嘖嘖... 真正解決花了我十分鐘的時間 (包含 REBOOT),不過在這之前卻懹我懊惱了好幾個小時...,這裡借題發洩一下 [:@]... 也給剛好也用 MCE 想換四核心 CPU 的人參考一下...

     

    這個故事告訴我們,沒事別亂換硬體,要換硬體請準備好耐心及奮戰到底的決心,不然就準備好重灌的準備... Orz

    2008/06/09 技術隨筆 有的沒的

  3. [RUN! PC] 2008 六月號

    IMG_8698 (Canon PowerShot G9)

     

    運氣不錯,還有續集耶 [:D]

    沒想到這些有點冷門的內容,雜誌社還願意刊... 真是太感謝了... 隔了兩個月刊出第二篇了。不過這次稿擠,最後簡介跟網址沒貼出來,讀者大概都看不到這邊吧,沒關係,看的到的就是有緣人...。

    一樣,一方面只是留個紀念,另一方面讓讀者 (如果真的有的話) 有個留話的地方。文章內提到的 SAMPLE CODE 在這裡。想要線上試試範例程式的可以到 這裡 執行看看。

    2008/06/04 RUN! PC 專欄文章 .NET ASP.NET RUN! PC 技術隨筆

  4. FlickrProxy #4 - 修正同步上傳的問題

    寫到這,越寫越拖抬了... 這次沒有加上任何 "新功能",有的只是修正使用上的一些問題而以...

     

    首先,還是要感謝愛用者 小熊子 的回報,照片初次被下載時會觸發上傳到 Flickr 的動作,上傳完成才重新引導 Request 到 Flickr 存取照片。如果在這一連串動作尚未完成前,就有第二個 Request 跑來的話,那這張照片就會被傳到 Flickr 兩次...。Orz,枉費我還投搞這些並行處理的文章,怎麼可以犯這種錯...

    就跟很多 BUG 一樣,難在沒想到,難在沒發現,難在不知道原因...,不然修正 BUG 倒是很簡單的事,感謝回報這個 BUG 的恩公... 找到問題後剩下的 ISSUE 就迎刃而解了,要做的就是把關鍵的程式碼包裝在臨界區 (CRITICAL SECTION) 內,以防這段 CODE 同時間執行多次。這段 CODE 不能太大,鎖定範圍太大會影響效能 (好不容易換了四核CPU,鎖太大就糟踏這顆 CPU 了...),最後找出關鍵應該是 [判定是否需要上傳到 FLICKR] 及 [建立 FLICKR 副本檔] 這兩個動作,一定要包括在內,拆開的話就不能保證結果正確了。

    修正過的程式,加上 LOCK 鎖定部份程式碼[copy code]
                string flickrURL = null;            lock (this.GetType())            {                if (File.Exists(this.CacheInfoFile) == false)                {                    //                    //  CACHE INFO 不存在,重新建立                    //                    this.BuildCacheInfoFile(context);                    context.Response.AddHeader("X-FlickrProxy", "Upload");                }            }
    
       1:  string flickrURL = null;
    
       2:  lock (this.GetType())
    
       3:  {
    
       4:      if (File.Exists(this.CacheInfoFile) == false)
    
       5:      {
    
       6:          //
    
       7:          //  CACHE INFO 不存在,重新建立
    
       8:          //
    
       9:          this.BuildCacheInfoFile(context);
    
      10:          context.Response.AddHeader("X-FlickrProxy", "Upload");
    
      11:      }
    
      12:  }
    

     

     

    寫好後,這段 CODE 越看越不順眼,雖然我的網站流量沒那麼大啦 [H],不過怎麼可以這麼短視... 這段 CODE 的問題還是一樣,鎖定的範圍 "太大了" !! 會嚴重影響效能.. (如果流量真的很大的話)

    怎麼說? 不過才兩行,那到底是要縮到多小? 不不,問題其實不在於 LOCK 的區段到底有多少 CODE,而是該 LOCK 的只有對同一張照片的 Http Request 才該被阻檔下來,同時間有多個 Http Request 來下載不同張照片,以現在的點閱率來說我應該要高興吧... 幹嘛還去 LOCK 它? 不過上面的 CODE 就是在做這件事,不分青紅皂白只要是有上傳到 FLICKR 的動作就一率 LOCK。更糟的是如果有一張照片正在上傳中,其它照片的 Http Request 也都會被迫停下來等它傳完...

     

    該要有個改進的版本了。LOCK過度也是初次碰到 Multi-threading Programming 的人很容易犯的錯誤,接下來看看第二個版本:

    修正過的版本,只會LOCK同一個檔案的REQUEST:[copy code]
                lock (this.LockHandle)            {                if (File.Exists(this.CacheInfoFile) == false)                {                    //                    //  CACHE INFO 不存在,重新建立                    //                    this.BuildCacheInfoFile(context);                    context.Response.AddHeader("X-FlickrProxy", "Upload");                }            }
    
       1:  lock (this.LockHandle)
    
       2:  {
    
       3:      if (File.Exists(this.CacheInfoFile) == false)
    
       4:      {
    
       5:          //
    
       6:          //  CACHE INFO 不存在,重新建立
    
       7:          //
    
       8:          this.BuildCacheInfoFile(context);
    
       9:          context.Response.AddHeader("X-FlickrProxy", "Upload");
    
      10:      }
    
      11:  }
    

     

    看起來只有第一行 LOCK STATEMENT 裡指定的物件不一樣而以。沒錯,這裡跟第一段 CODE 的差別只在於 LOCK 的標的物不一樣。同一個物件只能被 LOCK 一次,當物件被 LOCK 還沒放開時,第二個人想要 LOCK 同一個物件,很抱歉... 得先等第一個人願意放掉它才可以。LOCK不同物件就各不相干了。沒錯,這就是我要的邏輯。所以這個問題的關鍵在於,我如何讓每張照片有專屬的 "物件" 來 LOCK ?

    檔名的字串物件? 不適合... 可能有多個字串值相同,但是是不同物件... FileInfo? 也不行,因為找不到文件會保證同一個檔案拿到的 FileInfo 物件會是同一個... 沒辦法,只好自己弄一個。來看一下 LockHandle 的實作:

    LockHandle Property 實作的程式碼[copy code]
            private object LockHandle        {            get            {                string hash = this.GetFileHash();                lock (_locks)                {                    if (_locks.ContainsKey(hash) == false)                    {                        _locks.Add(hash, new object());                    }                }                return _locks[hash];            }        }        private static Dictionary<string, object> _locks = new Dictionary<string, object>();
    
       1:  private object LockHandle
    
       2:  {
    
       3:      get
    
       4:      {
    
       5:          string hash = this.GetFileHash();
    
       6:          lock (_locks)
    
       7:          {
    
       8:              if (_locks.ContainsKey(hash) == false)
    
       9:              {
    
      10:                  _locks.Add(hash, new object());
    
      11:              }
    
      12:          }
    
      13:          return _locks[hash];
    
      14:      }
    
      15:  }
    
      16:  private static Dictionary<string, object> _locks = new Dictionary<string, object>();
    

     

    其實以值來說,拿檔名就足夠拿來示別了,只不過有大小寫的問題要處理。拿檔案的內容做 MD5 實在有點殺雞用牛刀,不過因為處理照片本來就需要算檔案的 MD5 了,現成的就拿來用一下...。這裡我簡單的做了個 Dictionary,就放沒什麼用的 OBJECT,我只要這個 PROCESS 在有生之年,同一個檔案都會拿到同一個 OBJECT 就足夠了...

     

     

    都寫到這還有什麼不滿意的? 還是有... 哈哈。因為我在測試時有過一個頁面,同一頁會放一堆圖檔...。試想一下當我瀏覽這頁面會發生什麼事?

    "同時間 IE 發出了數個 HttpRequest 來跟我的程式要圖檔,如果正巧都是第一次,嗯,有限的頻寬要一次上傳多張圖檔到 Flickr,不就更慢了?"

    就算我的頻寬沒問題,同一瞬間這麼多 UPLOAD 的動作,引起 Flickr 的 "關切" 就不好了... 我是不是應該要限制一下同時上傳的數量才對? 就像 FlashGet 可以限制同時下載的數量一樣...

    哈,不就是之前寫過的文章,用 SEMAPHORE ? 沒錯... 怎麼老覺的這篇像在替我其它文章打廣告用的... 事實上不見得要動用到 SEMAPHORE,如果你要限制的是一次只能一個 UPLOAD 動作,直接用各種的 LOCK 機制就夠了。如果你要限制並行的 UPLOAD 動作是兩個以上,甚至更動態隨著 LOADING 變化等等,才需要動用到 SEMAPHORE ...

    既然要 DEMO,就 DEMO 實際一點的 CODE 吧。假設我要限制並行 UPLOAD 的數量不超過 2 個,則需要把 CODE 改成這樣。首先要先準備好 SEMAPHORE 物件:

    準備 SEMAPHORE,事先插好兩根旗子[copy code]
            public static Semaphore FlickrUploaderSemaphore = new Semaphore(2, 2);
    
       1:  public static Semaphore FlickrUploaderSemaphore = new Semaphore(2, 2);
    

     

    真正執行上傳動作的部份要加上 SEMAPHORE 的管控:

    用 SEMAPHORE 控制同步執行的數量[copy code]
                FlickrUploaderSemaphore.WaitOne();            photoID = flickr.UploadPicture(this.FileLocation);            FlickrUploaderSemaphore.Release();
    
       1:  FlickrUploaderSemaphore.WaitOne();
    
       2:  photoID = flickr.UploadPicture(this.FileLocation);
    
       3:  FlickrUploaderSemaphore.Release();
    

     

    嗯,真是小題大作,不過這種機會不拿來練習練習,真正碰到怎麼寫的出來? 如果各位對於在 ASP.NET 上怎麼使用 LOCK 及 SEMAPHORE 不大熟的,可以參考一下我投稿的文章... 萬分感謝 [:D]

    2008/06/03 作品集: FlickrProxy .NET ASP.NET 作品集

  5. FlickrProxy #3 - 終於搞定大圖網址錯誤的問題

     

    由於在使用 Flickr API 時, 老是碰到上傳成功後, 結果拿到的照片 URL 不能看的問題... 被它整了好久, 不過總算解決了 @_@... 原來 API 拿到的資料是錯的, 嘖...

    說來話長, 不過既然花時間解決了就要記錄一下... 問題大概是這樣. 上傳照片完成之後可以拿到 photoId, 代表某一張放在 Flickr 上的照片. 之後透過 PhotosGetInfo(photoId) 這個 API 可以取得這張照片的相關資訊, 當然也有 MediumUrl, LargeUrl... 等等 properties 可以用.

    很直覺嘛, 要秀大圖我就直接拿 LargeUrl 就好, 偏偏有時是好的, 有時是壞的... API 傳回來的東西應該不會錯吧? 我心裡是這樣想的. 不過跟 Flickr 網站的 url 對照一下才發現, 竟然有時是不同的... 一路追下去, google 跟作者在 codeplex 網站上的 forums 都找了, 才發現...

    PhotosGetInfo( ) 抓到的只是一堆 ID, 然後用 Flickr 公布的網址格式 "湊" 出各種 URL. 然而過去 Flickr 層經有一次改變部份網址的格式, 當你的圖檔不是很大時, Flickr 判定沒有另外存一張大圖的需要了, 就直接跳到原圖 (original size). 而原圖的網址格式又不一樣, 因此當圖檔太小時, API 抓到的 LargeUrl 就會是錯的...

    My God,.... 為了這種鳥問題害我多白了好幾根頭髮... 找到原因後找 solution 就簡單了. 因應這個問題, 也多了一組 API: PhotosGetSizes( ), 直接連回 Flickr 明確的查詢可用的 size 有幾種, 連同它的網址及一堆相關資訊一起傳回來... 改用這個 API 傳回的資訊, 結果就正確了, 沒有圖掛掉的問題... Orz

     

    不能怪人家 API 寫的不好, 只能怪自己功課沒作足... 不看文件直接拿 API 就用, 看名字猜用法才會這樣.. code 改一改就 ok 了, 少了這個不確定性, 原本畫蛇添足加上去的確認圖檔的動作也不用了.. 貼一下修改前跟修改後的 code:

     

    使用 PhotoInfo 物件 (可能會出現 photo not available) [copy code]
                PhotoInfo pi = flickr.PhotosGetInfo(photoID);            string flickrURL = null;            string size = null;            try            {                flickrURL = this.CheckFlickrUrlAvailability(pi.MediumUrl);                size = "medium";                flickrURL = this.CheckFlickrUrlAvailability(pi.LargeUrl);                size = "large";                flickrURL = this.CheckFlickrUrlAvailability(pi.OriginalUrl);                size = "original";            }            catch { }
    
       1:  PhotoInfo pi = flickr.PhotosGetInfo(photoID);
    
       2:  string flickrURL = null;
    
       3:  string size = null;
    
       4:  try
    
       5:  {
    
       6:      flickrURL = this.CheckFlickrUrlAvailability(pi.MediumUrl);
    
       7:      size = "medium";
    
       8:      flickrURL = this.CheckFlickrUrlAvailability(pi.LargeUrl);
    
       9:      size = "large";
    
      10:      flickrURL = this.CheckFlickrUrlAvailability(pi.OriginalUrl);
    
      11:      size = "original";
    
      12:  }
    
      13:  catch { }
    

     

     

    改用 PhotosGetSizes( ) API[copy code]
                foreach (Size s in flickr.PhotosGetSizes(photoID).SizeCollection)            {                XmlElement elem = null;                elem = cacheInfoDoc.CreateElement("size");                elem.SetAttribute("label", s.Label);                elem.SetAttribute("source", s.Source);                elem.SetAttribute("url", s.Url);                elem.SetAttribute("width", s.Width.ToString());                elem.SetAttribute("height", s.Height.ToString());                cacheInfoDoc.DocumentElement.AppendChild(elem);            }
    
       1:  foreach (Size s in flickr.PhotosGetSizes(photoID).SizeCollection)
    
       2:  {
    
       3:      XmlElement elem = null;
    
       4:      elem = cacheInfoDoc.CreateElement("size");
    
       5:      elem.SetAttribute("label", s.Label);
    
       6:      elem.SetAttribute("source", s.Source);
    
       7:      elem.SetAttribute("url", s.Url);
    
       8:      elem.SetAttribute("width", s.Width.ToString());
    
       9:      elem.SetAttribute("height", s.Height.ToString());
    
      10:      cacheInfoDoc.DocumentElement.AppendChild(elem);
    
      11:  }
    

     

    嗯, 終於搞定. FlickrProxy 正式邁入實用的階段... 收工!

     

     

     

    2008/05/21 作品集: FlickrProxy .NET ASP.NET 作品集