北京理工大學 20981 陳罡
目前隨著智能手機的不斷髮展、進化,手機功能越來越強大,處理器能力越來越高,配有200MHz,300MHz處理器的智能手機已經不是什麼鳳毛麟角,而是成為福士化的電子消費品了,這也為基於智能手機系統的第三方應用軟體開發商帶來了寶貴的發展機遇。眼看3G在即,未來手機上的平均頻寬應該能夠達到64K左右(這是從TD的角度來看的,如果採用WCDMA或者CDMA2000會更快一些),這遠遠超過了gprs傳統的平均頻寬9k的速度。手機應用開發領域應該會有一番“血戰”,開發商會絞盡腦汁設計出各自的“殺手級”的應用程式吸引使用者、豐富手機的功能,當然同時賺得鉑滿瓢滿。:D但是這一切都取決於是否能夠創造出使用者感興趣的應用。我個人覺得,如果3G的頻寬真的可以做得很大,一旦使用者可以很輕易地從行動電話通訊訪問到互連網,相比於互連網網站而言,傳統的free wap之類的網站無論從規模上還是內容豐富程度上都無法與互連網相媲美,而且內容同質化問題嚴重。互連網會對整個wap行業帶來一個非常大的衝擊。對於應用開發商來說也將面臨同樣的問題,我們舉個例子:對於傳統的wap browser開發商來說,互連網目前的css, web 2.0以及以youtube為代表的視頻類網站為wap browser提出了更高的要求。這相當於對此類應用的開發商提出了更高的要求。
目前多數開發商都致力於手機上RSS類應用的開發,對於低頻寬的gprs網路下這是無奈的事情。但是對於使用者來說,單單是幾條新聞、幾張圖片,這種模式無論從使用者體驗上,還是從使用者粘度上都不足以吸引使用者長期使用下去,通常面臨的問題就是使用者新鮮幾天就刪掉了。而且還要在後台做很多工作,如內容的準備,編輯,處理等等。既然如此,如果頻寬真的寬了,幹嘛不直接利用互連網的資源和使用者粘度來做自己的事情呢?
symbian平台上如果能夠支援youtube該有多好?於是從google上搜尋了一下,果然找到了一個開源的項目MobiFLV。這位開發人員已經開發MobiFLV有很長時間了,確實做了很多寶貴的工作。從代碼上來看,他是採用移植的方案,從open source的ffmpeg中將libavcodec摘出來,並且把只與flv格式相關的代碼移植到symbian平台上。目前經過測試,MobiFLV支援的FLV是h263+mp3或者mpeg4+mp3編碼的視頻格式,至於最新的支援h264的flv或者h263+aac格式的flv都是不支援的,這一點想要使用這個代碼的朋友要特別注意了。
下面內容是通過一番google得到的,關於flv中採用的h263視頻編碼跟標準是Sorenson h.263,因此,它與3gp檔案中採用的h263格式的編碼是有區別的:1、視頻tags的組成:tag類型 0x09tag資料大小 3個位元組的視頻資料大小tag時間戳記 3個位元組tag資料應用的時間(毫秒)tag時間戳記擴充 1個位元組的時間戳記擴充,讓時間戳記變成4位元組,本位元組作為時間戳記的高位.streamID 3個位元組的類id,總是02、視頻tags的資料:視頻tags資料和swf檔案格式中的VideoFrame是相似的.他們的資料是一樣的視頻格式的資料的組成如下:(1)框架類型 4bit1: 主要畫面格keyframe(視頻中的主要畫面格,資料存放區的是整個畫面完整的資料,可以提取它來產生圖片)2: 中間幀inter frame(主要畫面格之間的狀態,不完整的畫面資料,需要依靠前面幀的資料產生)3: 可任意使用的中間幀disposable inter frame(H.263 only)(2)視頻編碼id 4bit2: Sorenson H.263(mencoder轉換所使用的視頻編碼)3: Screen video4: On2 VP65: On2 VP6 with alpha channel6: Screen video version 2(3)視頻資料If CodecID = 2 Then H263VIDEOPACKETIf CodecID = 3 Then SCREENVIDEOPACKETIf CodecID = 4 Then VP6FLVVIDEOPACKETIf CodecID = 5 Then VP6FLVALPHAVIDEOPACIf CodecID = 6 Then SCREENV2VIDEOPACKET這裡說一下Sorenson H.263視頻編碼以及其資料包:從swf6開始,flash使用被稱作Sorenson H.263的視頻編碼格式,這種格式基於h.263,一個公開視頻編碼通訊協定由ITU(國際電信聯盟)提出的.想瞭解h.263編碼格式的朋友可以參考http://www.chinavideo.org/index.php?option=com_content&task=view&id=123&Itemid=0但是Sorenson H.263編碼和H.263是有差別的:下面的特性不存在Sorenson H.263中:■ GOB (group of blocks) layer■ Split-screen indicator■ Document camera indicator■ Picture freeze release■ Syntax-based arithmetic coding■ PB frames■ Continuous-presence multipoint■ Overlapped block-motion compensation下面的特性是Sorenson H.263增加的:■ Disposable frames (difference frames with no future dependencies)■ Arbitrary picture width and height up to 65535 pixels■ Unrestricted motion vector support is always on■ A deblocking flag is available to suggest the use of a deblocking filter關於這個MobiFLV,編譯起來是非常方便的。可以使用code warrior或者carbide c++進行編譯,我採用的sdk版本是symbian 3rd FP1,這裡需要注意的是,MobiFLV是需要安裝Open C外掛程式的,而做為libavcodec的解碼器本身是不需要的,這一點安排很有點諷刺意味,呵呵,真正有可能用到Open C的反而沒有用到,而前端卻又用到了。還有,似乎用vs2005+carbide.vs外掛程式的編譯環境不能夠工作。另外,這個版本的MobiFLV是支援RVCT的,也就是說是可以用abld build armv5 urel來產生手機上的測試版本的,RVCT是支援thumb 16位指令集的,難以想象支援FLV視頻的播放器編譯出來的sis包,才53K多一點。筆者已經使用N81手機真機測試通過。
從代碼結構上來看,MobiFLV主要分為兩個部分:(1)libavcodec_mobitubia(它是libavcodec解碼器的核心部分)完全是libavcodec中關於flv解碼部分的修改和移植,這部分工作確實做得非常出色,沒有記憶體泄露。
(2)MobiFLV(它是播放器的調用前端)這裡面需要重點關注Player.h扼Player.cpp,這裡面詳細(可以說是以教科書般的代碼,展示了libavcodec的調用方法)相信如果仔細閱讀了Player.cpp,關於“他是如何將視頻資料變成映像的?”,類似這樣的疑惑就全部一目瞭然了。
很多朋友對於視頻播放器中如何同步視頻和音頻資訊這塊非常困擾,下面我就來結合代碼做一個說明:
開始視頻播放主要是在CMobiFLVAppView::OfferKeyEventL()函數中使用如下代碼,開啟了一個CPeriodic,來快速周期性的快速調用CMobiFLVAppView::PlayLoop():
iPeriodic = CPeriodic::NewL(0); TInt tTickInterval = 1; iPeriodic->Start(tTickInterval, tTickInterval, TCallBack(PlayLoopTick,this));
|
而這個PlayLoop的實現如下:
TInt result = FetchCodecAndDecode(iPts, iNeoStreamPlayer); if (result == -3) // End { iPts = FLVGetDuration() * 1000; delete iPeriodic; iPeriodic = NULL; }
iNowTime.HomeTime(); iOffScreenBitmap->LockHeap(); if (DoWithDecoded((unsigned char*)iOffScreenBitmap->DataAddress(),EFalse,
iFirstFrameNum, iFirstFrameTimeStamp,
iFrameNum, iNowTime.Int64()) >MAX_FRAME_SKIP) { iFirstFrameNum = iFrameNum; iNowTime.HomeTime(); iFirstFrameTimeStamp = iNowTime.Int64(); DoWithDecoded((unsigned char*)iOffScreenBitmap->DataAddress(),
EFalse, iFirstFrameNum, iFirstFrameTimeStamp,
iFrameNum, iNowTime.Int64()); } FetchCodecAndDecodeSoundOnly(iPts, iNeoStreamPlayer, iAudioCurrentFTell); iOffScreenBitmap->UnlockHeap(); User::ResetInactivityTime(); DrawNow();
|
注意,這裡的關於時間處理的關鍵區段,就在於這個DoWithDecoded函數了。
int DoWithDecoded(unsigned char* dst, unsigned char no_pic,
intfirst_frame_num, int first_frame_timestamp,
int& frame_num, intnow_timestamp) { const int should_b_frame_num = first_frame_num +
(now_timestamp -first_frame_timestamp) *
flvGlobalVar.framerate * 0.000001;
const int diff = frame_num - should_b_frame_num; if (diff > 0)
return 0; if (!no_pic) { if (diff == 0) { if (g_RectWidth == AVIdx && g_RectHeight == AVIdy) ConvertYUV2BGRFast(avpict.data[0], avpict.data[1],
avpict.data[2], dst, avpict.linesize[0],
AVIdy, AVIdx); else ConvertYUV2BGRNeoFast(avpict.data[0], avpict.data[1],
avpict.data[2], dst,
avpict.linesize[0], AVIdy,
AVIdx, g_RectWidth,g_RectHeight); numframeskipped = 0; } else { numframeskipped++; } }
is_decoded = false; frame_num++;
return numframeskipped; }
|
密切關注它的前兩行:
should_b_frame_num = first_frame_num + (now_timestamp -first_frame_timestamp) *
flvGlobalVar.framerate * 0.000001;
這一行用來根據讀取出來framerate,幀率來計算每一幀需要停留多久,如果還沒有到下一幀的播放時間,那麼這裡的diff就過不去,就會return。呵呵,這裡就是音視頻的同步的精華了,只要有了framerate,剩下的直接計算出來,隨便用個timer控制時間即可,這當然也是由於h.263解碼本身的速度比較快,不會引起timestamp都到了,但是還沒有解完當前幀的情況,不需要做由於decoder引起的跳幀問題,這在h264中可能就有一些差別了。在此,還是比較簡單可行的,代碼也很清晰。
MobiFLV的剩餘部分,都是調用Player.h中開放的函數了,我個人以為,這個所謂的Player API應該做成一個類比較符合symbian風格了。這裡有一個檔案不能不提,那就是YUVToRGB.h和YUVToRGB.cpp,作者用了這麼個ConvertYUV2BGRNeoFast(),ConvertYUV2BGRNeoFastScaleUp(),ConvertYUV2BGRNeoFastScaleDown()呵呵,連駭客帝國中的Neo都用上了來取名字,自然表示了作者對這幾個函數的自信。執行效率自然很好,經過了大量的查表和手工位移最佳化,效率確實不錯。建議現在還在跟視頻播放速度這塊鬱悶的朋友可以試試他弄的這個方法。
any way,能夠工作就行。
下面是模擬器:
附上本文的原始碼,由於MobiFLV的官方網站收到國內網關的影響,基本上登入不上去。需要用代理才可以訪問,而這項技術又是國內開發人員非常關注和需要的。我在此就做個好人,把MobiFLV 1.0的全部原始碼貼出來供廣大開發人員使用:
|
檔案: |
MobiFLV Pack 1.00.zip |
大小: |
285KB |
下載: |
下載 |
|
代碼的編譯和使用方法見程式碼封裝中的Release Note。順便把我自己編譯出來的沒有簽過名的sis包也貼出來,供感興趣的朋友下載、測試。
|
檔案: |
MobiFLV.rar |
大小: |
51KB |
下載: |
下載 |
|
注意,使用前一定要往手機C://Data//Videos//目錄下面拷貝一個叫做Test.flv的視頻,否則會Leave。