手機音視頻流媒體開發一些有用資料

目錄:

1.CVideoPlayerUtility播放RTSP視頻流

2.關於symbian流媒體播放

3.基於http的流媒體音樂播放器的開發

4. Symbian解碼AMR的方法

5. 怎麼樣用C語言實現amr解碼爲pcm數據

6. Something about AAC

7. 3gp文件格式研究

8. mplayer音視頻同步原理

9.  修改TCPMP界面

10.windows mobile使用libmad解碼mp3流媒體

11.mp4文件格式

12. MMFCODEC AAC解碼器配置

13. 移動wap網關的一些限制說明

14. MTK平臺用Socket實現HTTP請求總結

 

 

1. 通過CVideoPlayerUtility的OpenUrlL()打開了3gp (證實了使用該接口可以實現RTSP流的接收,而官方文檔只是介紹能播放"clip”)

 

   相關鏈接:如何使用CVideoPlayerUtility來播放視頻文件

 

  OpenUrlL()播放rtsp資源時候,大致會做下面3件事情
1.根據提供的文件格式,如果url沒有文件格式,需要手動指明mime type;初始化plug-in codec;在MvpuoOpenComplete()回調之前完成
2.通過內置的rtsp協議與服務器通信,建立連接,這部分在MvpuoPrepareComplete()之前完成
  在我的例子中,模擬器和dss建立了rtsp連接,模擬器解析了sdp,這樣就得到了文件信息
3.發出rtsp的PLAY請求,模擬器開始接受數據,這時候可以通過MvloLoadingStarted()得到開始緩衝的信息
 
最重要的調用就是這個了
iPlayer->OpenUrlL(aUrl, iap, KNullDesC8, KNullUid);
 
在開始寫這行代碼之前,要確定
1.aUrl在realplayer上能不能打開,協議是否支持,傳輸的方式(TCP/UDP)和端口是否支持,文件的格式是否支持
2.iap是否設置正確,如果不正確不能建立連接
3.在模擬器上要加上函數需要的能力,因爲我打開了能力檢查,沒再mmp中加入能力,導致回給我KErrCouldNotConnect

 

 

 

 試驗當中aUrl等於rtsp://192.168.20.43/sample_50kbit.3gp,這個文件採用MPEG4-ES + AMR編碼格式

 

  轉自仙人闆闆BLOG

 

2. 無論使用OpenFileL()、OpenDesL()還是OpenUrlL(),傳給CVideoPlayerUtility的都只是視頻剪輯,所以說它支持播放剪輯式的內容。而開發人員有辦法像使用CMdaAudioOutputStream那樣把流式的內容連續地傳給CVideoPlayerUtility播放嗎?沒有。所以說它不支持播放流式的內容。 from chenziteng

 

3.基於http的流媒體音樂播放器的開發

 

     原理是這樣:從 HTTP 服務器獲得 MP3 媒體信息,然後通過網絡傳輸把 MP3 數據以數據流的形式接收到 MP3 流媒體播放器客戶端,由客戶端通過 libmad 解碼 MP3 數據流,得到 PCM 音頻數據,寫入音頻設備,播放音樂。

 

 

 

   考慮方案是CMdaAudioOutputStream結合Libmad(開源MP3解碼庫)來實現。有個開源項目S60 Internet Radio很有用,它支持多種音頻格式的播放:MP3, AAC, eAAC+, MP4, M4A, WMA, 3GPP, AMR, and WAV。

 

4. Symbian解碼AMR的方法

  1、直接使用CMdaAudioOutputStream類
CMdaAudioOutputStream *iOutputStream;

在函數MaoscOpenComplete(TInt aError)中設置解碼器爲AMR
iOutputStream->SetDataTypeL(KMMFFourCCCodeAMR)

需要包含的頭文件
#include <mda/common/audio.h>
#include <MdaAudioOutputStream.h>

需要鏈接的庫
mediaclientaudiostream.lib

然後就可以直接使用iOutputStream->WriteL寫入AMR碼流。
但這種方法無法實現流式播放,可能是因爲每次調用WriteL都會重新初始化解碼器,回放出來的聲音有問題。

 


2、使用CMMFCodec類解碼,然後再使用CMdaAudioOutputStream類回放

CMMFDescriptorBuffer *iPcm16Buffer = CMMFDescriptorBuffer::NewL( OutputSize );

CMMFDescriptorBuffer *iAmrBuffer = CMMFDescriptorBuffer::NewL( InputSize );
CMMFCodec *iOCodec = CMMFCodec::NewL( TUid::Uid(  0x101FAF67 ) );

TDesC8* ConvertAmr2PcmL( const TDesC8& aAmrData )
{

 // amr input data
 iAmrBuffer->Data().Copy( aAmrData );

 // decode
 TCodecProcessResult result = iOCodec->ProcessL( *iAmrBuffer, 
*iPcm16Buffer );

 if ( ( result.iStatus == TCodecProcessResult::EProcessComplete ) && 
 ( result.iSrcBytesProcessed == KAmrFrameSize ) &&
 ( result.iDstBytesAdded == KPcmBufferSize ) )
 {
 // return buffer
  return &( iPcm16Buffer->Data() );
 }
 else
  return NULL;
}

需要包含的頭文件
#include <mmf/server/mmfcodec.h>

需要鏈接的庫
mmfserverbaseclasses.lib

使用CMdaAudioOutputStream的時候要注意,必須等MaoscBufferCopied被調用以後才能再
次調用WriteL,因爲Symbian中沒有緩衝隊列的。

 

5. 怎麼樣用C語言實現amr解碼爲pcm數據

 

3GPP TS 26.073 AMR speech Codec; C-source code
http://www.3gpp.org/FTP/Specs/html-info/26073.htm

3GPP TS 26.104 ANSI-C code for the floating-point Adaptive Multi-Rate (AMR) speech codec
http://www.3gpp.org/FTP/Specs/html-info/26104.htm

 

6. Something about AAC(轉自http://blog.csdn.net/Kryptonum)

關於AAC的基本概念都可以在wikipediaAudio Coding找到,就說幾個不太好理解的問題。(部分內容來自doom9.net)

1。文件頭

AAC有多種存儲的方式,分別是ADTS文件頭加RAW Data Block,ADIF加RAW Data Block,LATM加RAW Data Block;或者就是將RAW Data直接存儲在MP4容器格式裏。

ADTS (Audio Data Transport Stream) 適用於用於網絡傳輸; ADIF (Audio Data Interchange Format) 則主要用於本地存儲。ADTS文件頭存在於每一個RAW Data Block前或者每隔2-4個RAW Data Block前,以確保網絡傳輸的健壯性;而ADIF文件頭僅存在於文件的起始處。

2.容器格式

容器格式允許你將不同種類的多媒體數據流(多爲視頻流和音頻流)合併在一個單一的文件內。
多媒體容器格式,就是我們熟知的AVI(.avi), MPEG(.mpg, .mpeg), Matroska(.mkv, .mka), OGM(.ogm), Quicktime(.mov),或Realmedia(.rm, .rmvb).

MP4是遵循MPEG-4(ISO 14496-14)的官方容器格式定義的廣義文件擴展名。它可以流媒體化並支持衆多多媒體的內容(多音軌(multiple audio)、視頻流(video)、字幕(subtitlestreams)、圖片(pictures)、可變楨率(variable- framerates)、碼率(bitrates)、採樣率(samplerates)等)和高級內容(advanced content)(官方稱之爲“Richmedia”(超媒體)或“BIFS”(Binary Format for Scenes/二進制格式場景),類似2D和3D圖形,動畫、用戶界面、類DVD菜單等。

 

3.擴展名 mp4 m4a aac

-.mp4:唯一的mpeg-4官方擴展名,支持所有音頻和視頻以及高級內容(或它們的混合) 

其他相關的擴展名:
- .m4v:是.mp4文件的錯誤擴展名,由apple提出,支持視頻+音頻,m4v擴展名可以安全地更名爲.mp4
- .m4a:是.mp4文件的錯誤擴展名,由apple提出,只支持音頻,m4a擴展名可以安全地更名爲.mp4
- .m4p:隨iTunes發售的DRM(Digital Rights Management/數字版權保護技術)產權保護的文件,使用Apple開發的DRM sheme
- .m4e:由.sdp修改擴展名的來的文件,Envivio用其於流媒體播放。
- .m4v, -.mp4v, -.cmp, -.divx, .-xvid, .264:raw的mpeg-4視頻流(並非內含於mp4)。
- .3gp, -.3g2:手機中使用的格式,其中儲存的內容同樣在.mp4未被定義(H.263, AMR(Adaptive Multi Rate/自適應多碼率))。

-.aac:是aac音頻文件的擴展名,不屬於mpeg-4定義的容器格式(由MPEG-2 part7定義),一般包含ADTS或ADIF文件頭

 

4.將AAC音頻文件MUX進入MP4容器格式會發生什麼

文件體積會減小,比特率大約降低3kbps,原因在於ATDS文件頭在mux的過程中被去除了。也就是說,存儲與容器格式中的aac音頻本身是沒有文件頭的,而是依賴於mp4文件的文件頭。加上第3點中對文件後綴的描述,就不難理解m4a文件爲什麼不能正常播放了,一方面之前所使用的Deocder依賴於ADTS或者ADIF文件頭,以此來獲取文件信息,另一方面無法識別容器格式的文件頭,自然無法正常解碼了。

 

 

7. 3gp文件格式研究 (轉windcao的專欄)

 

序言

06我開始做3gp文件的播放器,但是關於3gp的文檔太少了也很難找,在網友luxh的幫助下,
我終於有了第一份關於3gp文件格式的文檔《ISO/IEC 14496-12,ISO媒體文件格式》.
在此真心感謝luxh的貢獻.
當然了是英文版的,有文檔就不錯了.爲了便於查閱和理解,我把之後陸續找到的其他幾個文檔也揉在了一起.
從06年5月12日到現在2007-3-23,一點點的擠時間,總算寫完了.如有錯誤,敬請斧正.
之所以寫這篇文章目的只有一個,希望大家都能夠少走彎路.
有用的到的人可以隨意複製,轉貼.
因爲以後我有可能會對本文修改更新,所以請保留文中的原始鏈接.
願意同我一道研究的人請加msn:[email protected]

需要看的文檔
http://www.3gpp.org/ftp/Specs/archive/26_series/
3GPP TS 26.233
3GPP TS 26.243
3GPP TS 26.244


luxh找到的一個好東西
http://isotc.iso.org/livelink/livelink/fetch/2000/2489/Ittf_Home/PubliclyAvailableStandards.htm
大家一定要仔細找找啊,寶藏!
我們研究3gpp文件最重要的兩個文檔就是《ISO/IEC 14496-12,ISO媒體文件格式》和《3GPP TS 26.244-700》


ISO/IEC 14496的組成如下:(引自:http://www.blogcn.com/user73/lipingfu/index.html
(1)ISO/IEC 14496-1,系統部分,描述了組成一個場景的音頻和視頻成分之間的關係。
(2、3)ISO/IEC 14496-2,視頻部分和ISO/IEC 14496-3音頻部分,分別規定自然的和合成的視頻對象、音頻對象的編碼表示。
(4)ISO/IEC 14496-4,一致性測試部分,定義了比特流和設備的一致性條件,用來測試MPEG-4的實現。
(5)ISO/IEC 14496-5,參考軟件,包括與MPEG-4的主要部分相對應的軟件。
(6)ISO/IEC 14496-6,多媒體傳送整體框架DMIF,這是MPEG-4應用層與傳輸網絡的接口,定義了通信協議,使MPEG-4系統的數據流能進入各種傳輸網絡。還包含一個存儲格式MP4,用於存儲編碼的場景。
(7) ISO/IEC 14496-7,爲MPEG-4工具優化軟件,提供了對實現進行優化的例子(這裏的實現指的是第五部分)。
(8)ISO/IEC 14496-8,定義了在IP網絡上傳輸MPEG-4內容的方式。
(9)ISO/IEC 14496-9,爲參考硬件描述,提供了用於演示怎樣在硬件上實現本標準其他部分功能的硬件設計方案。
(10)ISO/IEC 14496-10,高級視頻編碼AVC,定義了一個被稱爲AVC的視頻編解碼器。
(11)ISO/IEC 14496-11,場景描述和應用引擎。
(12)ISO/IEC 14496-12,ISO媒體文件格式,定義了一個存儲媒體內容的文件格式。
(13)ISO/IEC 14496-13,知識產權管理和保護(IPMP)擴展。
(14)ISO/IEC 14496-14,MP4文件格式,定義了基於第十二部分的用於存儲MPEG-4內容的容器文件格式。
(15)ISO/IEC 14496-15,AVC文件格式,定義了基於第十二部分的用於存儲第十部分的視頻內容的文件格式。
(16)ISO/IEC 14496-16,動畫框架擴展AFX(Animation Framework eXtension)。
(17)ISO/IEC 14496-17,同步文本字幕格式(尚未完成,2005年1月達成"最終委員會草案"。
(18)ISO/IEC 14496-18,字體壓縮和流式傳輸(針對公開字體格式)。
(19)ISO/IEC 14496-19,綜合材質流(Synthesized Texture Stream)。
(20)ISO/IEC 14496-20,簡單場景表示(尚未完成,2005年1月達成"最終委員會草案"。
(21)ISO/IEC 14496-21,用於描繪(Rendering)的MPEG-J拓展(尚未完成,2005年1月達成"委員會草案"

我做的一個輔助工具http://download.csdn.net/source/162659 3gpp文件結構查看器
目前最新版本是0.1.2

正文:
首先來說3gp文件相當於一個容器,本身沒有什麼具體的編碼解碼規則。
我們可以選擇編碼方式 
AMR narrow-band:編碼簡稱'samr' 常用與語言片段的壓縮,可以對聲音片段進行最大程度的壓縮,但是失真較大,如果用在音樂文件上結構常常是無法忍受的。

(詳情請參考:3GPP TS 26.071: "Mandatory Speech CODEC speech processing functions; AMR Speech CODEC; General description".)

AMR wideband:編碼簡稱'sawb' 相對AMR narrow-band來說壓縮比降低了,品質有所提升可用來壓縮音樂。

(詳情請參考:3GPP TS 26.171: "AMR Wideband Speech Codec; General Description".)

Extended AMR-WB codec編碼簡稱 'sawp'

(詳情請參考:
3GPP TS 26.290: "Extended AMR Wideband codec; Transcoding functions".
3GPP TS 26.304: "ANSI-C code for the Floating-point; Extended AMR Wideband codec".
3GPP TS 26.273: "ANSI-C code for the Fixed-point; Extended AMR Wideband codec".

Enhanced aacPlus and MPEG-4 AAC codec編碼簡稱 'mp4a'

(詳情請參考:
3GPP TS 26.401: "General audio codec audio processing functions; Enhanced aacPlus general audio codec; General description".
3GPP TS 26.410: "General audio codec audio processing functions; Enhanced aacPlus general audio codec; Floating-point ANSI-C code".
3GPP TS 26.411: "General audio codec audio processing functions; Enhanced aacPlus general audio codec; Fixed-point ANSI-C code".

MPEG-4 video codec編碼簡稱'mp4v'

(詳情請參考:ISO/IEC 14496-2:2004: "Information technology – Coding of audio-visual objects – Part 2: Visual".)

H.263 video codec編碼簡稱'h263'

(詳情請參考:ITU-T Recommendation H.263 (01/05): "Video coding for low bit rate communication".)

H.264 video codec編碼簡稱'avc1'

(詳情請參考:ITU-T Recommendation H.264 (03/05): "Advanced video coding for generic audiovisual services"
ISO/IEC 14496-10:2005: "Information technology – Coding of audio-visual objects – Part 10: Advanced Video Coding".)

3GPP timed text format 編碼簡稱'tx3g'

(詳情請參考:3GPP TS 26.245: "Transparent end-to-end packet switched streaming service (PSS); Timed text format".)

其中手機最普遍支持的格式是 amr(音頻) +h263(視頻)

 

 3gp文件基於mpeg4由若干個box組成

 一個3gp文件由若干個box組成常見的有:

 

 

文件類型包:(FileTypeBox,簡稱代碼'ftyp')
ftyp:文件類型包相當於文件頭,說明了文件所使用的協議版本,編碼格式等信息
+[4]-+[4]-+[4]-+[4]-+[4]-+
|size|ftyp|mjbr|mivs|cpbr|
+----+----+----+----+----+
mjbr:major_brand 版本分支
mivs:minor_version 版本號 
cpbr:compatible_brands 兼容分支

媒體數據包:(Media Data Box,簡稱代碼'mdat')
"Media Data Box 
Box類型: ‘mdat’ 
容器: 文件 
是否必須: 否 

數量: 任意個. "-luxh

3gp file frame

mdat box 存放了音頻視頻和其他的數據,一般的文件至少有2個mdatbox, 一個用於音頻,一個用於視頻, 通常還會有一些文本信息也放在mdatbox中,各種信息的順序不固定。 如果只是存放音樂一個mdat就夠用了。

你可能會問這麼多box 都叫mdat我怎麼知道音頻放哪裏視頻放哪裏呢?彆着急這些相關信息都放在moovbox裏面

影片包:(moov box:Movie Box:)是一個3gp文件中最複雜最重要的文件。

moov box

看到這裏你可能會問"moovbox裏面的方框都分別代表什麼含義呀?媒體描述信息是怎麼存放的?",彆着急咱們來慢慢分析一下。
"Movie Box 
Box類型: 'moov'; 
容器: 文件 
是否必須: 是 
數量: 一個,並且只能是一個. 

媒體的原始數據被放置在這個box中,這個box位於文件的最高級別,一般來說這個box接近於文件的開始或者末尾,儘管這並不被要求。語法如下: 
a ligned(8) class MovieBox extends Box(‘moov’) {} "-luxh

moovbox 有兩個必要的子box他們是影片頭包和軌跡包
影片頭包movie header,簡稱代碼'mvhd' 
首先剖析一下影片頭包,順便以此爲例理解一下包的結構。
每一個包開頭的4個字節都是一個整數存放了本包的長度。
接下來的4個字節是個字符串存放了本包的類型。如'moov','mvhd'。
基本上包都是這模樣的:
+[4]-+[4]-+[size-8]-------------------+
|size|type|data                       |
+----+----+---------------------------+

很多box是這樣的我們可以稱之爲全包(full box)
+[4]-+[4]-+-+---+[size-8]-------------------+
|size|type|v|flg|data                       |
+----+----+-+---+---------------------------+

簡記爲
+[4]-+[4]-+[4]-+[size-8]-------------------+
|size|type|vsfl|data                       |
+----+----+----+---------------------------+

其中vsfl:版本號標誌

+-+---+
|v|flg|
+-+---+
v :version
flg:flages

在影片頭包中接下來的是版本標誌等信息。
MVHD 是媒體信息頭,存放媒體的全局性的信息。
+[4]-+[4]-+[4]-+[4]-+[4]-+[4]-+[4]-+[76]----...---------+[4]-+
|size|mvhd|vsfl|cttm|mdtm|tmsc|mxtl|reserved...         |ntid|
+----+----+----+----+----+----+----+--------...---------+----+
size:box長度
type:文件類型標識內容爲"mvhd"
vsfl:版本號標誌
cttm:creat time 文件創建時間
mdtm:modification time  文件修改時間
tmsc:timescale 時間縮放因數
mxtl:maxTrackLen duration of longest track 最長播放時間
reserved: 保留字段
ntid-next trak id  下一個頻道標識

軌跡包TRAK 也是一個容器,是單個媒體流頻道的信息的容器,它有兩個必要的子box:TKHD,MDIA。
TKHD 存放本trak的信息,有兩個版本
v=0
+[4]-+[4]-+[4]-+[4]-+[4]-+[4]-+[4]-+[4]-+
|size|tkhd|vsfl|cttm|mdtm|tkid|resv|duat|
+----+----+----+----+----+----+----+----+
+[12]--------+2-+2-+[36]----...-+[4]-+[4]-+
|reserved    |ct|rs|reserved    |twvo|thvo|
+------------+--+--+--------...-+----+----+
v=1
+[4]-+[4]-+[4]-+[8]-----+[8]-----+[4]-+[4]-+[8]-----+
|size|tkhd|vsfl|cttm    |mdtm    |tkid|resv|duat    |
+----+----+----+--------+--------+----+----+--------+
+[12]--------+2-+2-+[36]----...-+[4]-+[4]-+
|reserved    |ct|rs|reserved    |twvo|thvo|
+------------+--+--+--------...-+----+----+

size:box長度
type:文件類型標識內容爲"tkhd"
vsfl:版本號標誌
cttm:creat time 文件創建時間
mdtm:modification time  文件修改時間
tkid:track-id 同一個文件中這是一個不重複的序列
resv:reserved 保留字段
duat:duration 總的播放時間長度
reserved: 保留字段
ct:codec_type {audio=0x0100; video=0} 編碼類型,到底是音頻還是視頻等
rs:reserved 保留字段
reserved: 保留字段
如果這個track 是視頻編碼它將有如下字段,在你解碼的時候非常有用.
twvo:Track width , for visual only 視頻的寬度
thvo:Track height, for visual only 視頻的高度

MDIA是存放具體的媒體信息的容器。
有且僅有3個子box:{MDHD,HDLR,MINF}

MDHD媒體頭,也有兩個版本
 v=0
+[4]-+[4]-+[4]-+[4]-+[4]-+[4]-+
|size|type|cttm|mdtm|tmsk|duat|
+----+----+----+----+----+----+

v=1
+[4]-+[4]-+[8]-----+[8]-----+[4]-+[8]-----+
|size|type|cttm    |mdtm    |tmsk|duat    |
+----+----+--------+--------+----+--------+
其中
size:box長度
type:文件類型標識內容爲"mdhd"

pl:pad&language{bit(1) pad = 0;unsigned int(5)[3] language // ISO-639-2/T language code參見附錄}
*-*[15]-----------*
|p|language       |
*-*---------------*

pd:unsigned int(16) pre_defined = 0;

HDLR 句柄,描述媒體類型
+[4]-+[4]-+[4]-+[12]--------+[size-24]--+
|size|pred|hdlt|reserved    |name       |
+----+----+----+------------+-----------+
size:box長度
type:文件類型標識內容爲"tkhd"

pred:pre_defined = 0;
hdlt:handler_type;
 ‘vide’ Video track 視頻
 ‘soun’ Audio track 音頻
 ‘hint’ Hint track 註釋
reserved: reserved = 0;
name: 名稱字符串,0結尾的UTF-8串

MINF 媒體信息容器(Media Information Box)
這是一個普通的box容器.它的內部可能包含如下的子box:
VMHD,SMHD,HMHD,NMHD,DINF,STBL.
VMHD,SMHD,HMHD分別對應於視頻,音頻,注視,NMHD我還不太清楚.它們都屬於fullbox.
DINF數據信息和STBL採樣表,都是普通的box. VMHD還包括兩個數據字段.
+[4]-+[4]-+[4]-+[4]-+
|gmod|opcl          |
+----+----+----+----+
 
gmod:graphicsmode 描述了本視頻track 與其他視頻track的混合方式.默認的值爲0,也就是直接覆蓋.
opcl:opcolor 透明色顏色值 (red, green, blue)如果gmod不是copy的話會用到.
SMHD包括兩個字段 
+--+--+
|bl|rs|
+--+--+
 
bl:balance 是一個定點小數(精度 8.8) 前8bits是整數,後8bits是小數.如果值爲0說明左右聲道是相同的.全左的情況下值爲-1.0 全右則爲1.0.
rs:reserved 保留字段.
HMHD包括5個字段.如下:
+--+--+[4]-+[4]-+[4]-+
|mp|ap|mbrt|abrt|resv|
+--+--+----+----+----+
 
mp:maxPDUsize 最大PDU長度 -pdu是啥???????? 知道啥是pdu的朋友請告訴我.
ap:avgPDUsize 平均PDU長度 
mbrt:maxbitrate 最大比特率 
abrt:avgbitrate 平均比特率 
resv:reserved 保留字段 

NMHD是個空的fullbox 

DINF是一個普通的box,也是一個容器,它包括url,urn,dref三個fullbox
url 內部是一個UTF-8編碼的0結尾的字符串
string location;
url裏面則是兩個
string name;
string location;
這兩個都被稱爲DataEntryBox.
dref裏面是一個url或者urn的列表.首先它有一個字段
unsigned int(32) entry_count;DataEntryBox的列表的個數.
然後就是DataEntryBox的列表

STBL是一個普通的box,也是一個容器,裏面包含了很多媒體採樣信息.

STTS是一個fullbox裏面包含了採樣的時間長度信息
內部的數據首先是列表長度
unsigned int(32) entry_count;
然後就是採用時長列表.
列表每一項都由兩個字段組成.
unsigned int(32) sample_count;採樣個數
unsigned int(32) sample_delta;每個採樣的時間長度.

在認識stsd之前我們首先要了解一個數據結構SampleEntry和它的子類AudioSampleEntry,VisualSampleEntry和HintSampleEntry

sample_e

SampleEntry 是一個繼成box的抽象的數據結構模型.
除了size,type外它包括兩個字段,如下:
+[4]-+[4]-+[6]---+--+
|size|type|resved|di|
+----+----+------+--+
resved:reserved,保留字段
di:data_reference_index,序號.
從這個抽象數據結構繼承下來的三個子類分別增加了幾個獨特的新字段.

HintSampleEntry
+[4]-+[4]-+[6]---+--+[n]-+
|size|type|resved|di|data|
+----+----+------+--+----+
resved:reserved,保留字段
di:data_reference_index,序號.
data:是一個0結尾的utf8編碼的字符串.

VisualSampleEntry
+[4]-+[4]-+[6]---+--+--+--+[12]---------+--+--+[4]-+[4]-+[4]-+--+[32]----...---+--+--+
|size|type|resved|di|pd|rs|pre_defined |wd|ht|hrsl|vrsl|resv|fc|compressorname|dp|pd|
+----+----+------+--+--+--+-------------+--+--+----+----+----+--+--------...---+--+--+
size:box長度
type:box:類型
resved:reserved,保留字段
di:data_reference_index,序號.
pd: pre_defined 保留字段
rs: reserved 保留字段
pd: pre_defined 保留字段
wd: width 視頻的寬
ht: height 視頻的高
hrsl: horizresolution 水平分辨率如0x00480000; // 72 dpi
vtsl: vertresolution 垂直分辨率如0x00480000; // 72 dpi
rd: reserved 保留字段
fc: frame_count 每個採樣裏面的貞數,一般是1;
cmpn: compressorname 是一個數字開頭的字符串.並且末尾有填料.對齊到32位.
  +-+[n]-+[x]-+
  |n|data|xpad|
  +-+----+----+
n:number of data.數據的長度,x+n+1=32
dp: depth 視頻的色深 0x18 表示24位色
pd: pre_defined 保留字段

AudioSampleEntry
+[4]-+[4]-+[6]---+--+[8]-----+--+--+--+--+[4]-+
|size|type|resved|di|reserved|cc|ss|pd|rs|sprt|
+----+----+------+--+--------+--+--+--+--+----+

resved:reserved,保留字段
di:data_reference_index,序號.
reserved:保留字段
cc: channelcount 聲道數1或者2;
ss: samplesize 採樣位數大小 8bit 8 ;16bit 16;
pd: pre_defined 保留字段
rs: reserved保留字段
sprt:samplerate 採樣率

stsd是一個採樣包(SampleEntry)的列表,一般來講同一個列表中只有一種SampleEntry.
可以通過hdlr裏面的handler_type來判斷屬於哪種SampleEntry.
內部的數據首先是列表長度,一個32位無符號整數 entry_count;
然後就是採樣包列表.
採樣包有很多具體的實現.如下:

+MP4VisualSampleEntry:MP4視頻採樣包.
+MP4AudioSampleEntry:Entry type for audio samples defined in the MP4 specification.
+AMRSampleEntry:Entry type for AMR and AMR-WB speech samples defined in clause 6.5 of the present document.
+AMRWPSampleEntry:Entry type for AMR and AMR-WB speech samples defined in clause 6.5 of the present document.
+H263SampleEntry:Entry type for H.263 visual samples defined in clause 6.6 of the present document.
+AVCSampleEntry:Entry type for H.264 (AVC) visual samples defined in the AVC file format specification.
+TextSampleEntry:Entry type for timed text samples defined in the timed text specification
ISO/IEC 14496-17,同步文本字幕格式 歌詞可以放在這個裏面.
+HintSampleEntry:Entry type for hint track samples defined in the ISO specification.

附錄:
ISO-639-2/T language code

<table border="1" cellspacing="0" cellpadding="4" style="width: 621px; "><tbody><tr valign="top" bgcolor="#ffffee"><th width="25%" scope="col">ISO 639-2 Code</th><th width="25%" scope="col">ISO 639-1 Code</th><th width="25%" scope="col">English name of Language</th></tr><tr valign="top"><td scope="row">aar</td><td>aa</td><td>Afar</td></tr><tr valign="top"><td scope="row">abk</td><td>ab</td><td>Abkhazian</td></tr><tr valign="top"><td scope="row">ace</td><td> </td><td>Achinese</td></tr><tr valign="top"><td scope="row">ach</td><td> </td><td>Acoli</td></tr><tr valign="top"><td scope="row">ada</td><td> </td><td>Adangme</td></tr><tr valign="top"><td scope="row">ady</td><td> </td><td>Adyghe; Adygei</td></tr><tr valign="top"><td scope="row">afa</td><td> </td><td>Afro-Asiatic (Other)</td></tr><tr valign="top"><td scope="row">afh</td><td> </td><td>Afrihili</td></tr><tr valign="top"><td scope="row">afr</td><td>af</td><td>Afrikaans</td></tr><tr valign="top"><td scope="row">ain</td><td> </td><td>Ainu</td></tr><tr valign="top"><td scope="row">aka</td><td>ak</td><td>Akan</td></tr><tr valign="top"><td scope="row">akk</td><td> </td><td>Akkadian</td></tr><tr valign="top"><td scope="row">alb/sqi</td><td>sq</td><td>Albanian</td></tr><tr valign="top"><td scope="row">ale</td><td> </td><td>Aleut</td></tr><tr valign="top"><td scope="row">alg</td><td> </td><td>Algonquian languages</td></tr><tr valign="top"><td scope="row">alt</td><td> </td><td>Southern Altai</td></tr><tr valign="top"><td scope="row">amh</td><td>am</td><td>Amharic</td></tr><tr valign="top"><td scope="row">ang</td><td> </td><td>English, Old (ca.450-1100)</td></tr><tr valign="top"><td scope="row">anp</td><td> </td><td>Angika</td></tr><tr valign="top"><td scope="row">apa</td><td> </td><td>Apache languages</td></tr><tr valign="top"><td scope="row">ara</td><td>ar</td><td>Arabic</td></tr><tr valign="top"><td scope="row">arc</td><td> </td><td>Aramaic</td></tr><tr valign="top"><td scope="row">arg</td><td>an</td><td>Aragonese</td></tr><tr valign="top"><td scope="row">arm/hye</td><td>hy</td><td>Armenian</td></tr><tr valign="top"><td scope="row">arn</td><td> </td><td>Araucanian</td></tr><tr valign="top"><td scope="row">arp</td><td> </td><td>Arapaho</td></tr><tr valign="top"><td scope="row">art</td><td> </td><td>Artificial (Other)</td></tr><tr valign="top"><td scope="row">arw</td><td> </td><td>Arawak</td></tr><tr valign="top"><td scope="row">asm</td><td>as</td><td>Assamese</td></tr><tr valign="top"><td scope="row">ast</td><td> </td><td>Asturian; Bable</td></tr><tr valign="top"><td scope="row">ath</td><td> </td><td>Athapascan languages</td></tr><tr valign="top"><td scope="row">aus</td><td> </td><td>Australian languages</td></tr><tr valign="top"><td scope="row">ava</td><td>av</td><td>Avaric</td></tr><tr valign="top"><td scope="row">ave</td><td>ae</td><td>Avestan</td></tr><tr valign="top"><td scope="row">awa</td><td> </td><td>Awadhi</td></tr><tr valign="top"><td scope="row">aym</td><td>ay</td><td>Aymara</td></tr><tr valign="top"><td scope="row">aze</td><td>az</td><td>Azerbaijani</td></tr><tr valign="top"><td scope="row">bad</td><td> </td><td>Banda</td></tr><tr valign="top"><td scope="row">bai</td><td> </td><td>Bamileke languages</td></tr><tr valign="top"><td scope="row">bak</td><td>ba</td><td>Bashkir</td></tr><tr valign="top"><td scope="row">bal</td><td> </td><td>Baluchi</td></tr><tr valign="top"><td scope="row">bam</td><td>bm</td><td>Bambara</td></tr><tr valign="top"><td scope="row">ban</td><td> </td><td>Balinese</td></tr><tr valign="top"><td scope="row">baq/eus</td><td>eu</td><td>Basque</td></tr><tr valign="top"><td scope="row">bas</td><td> </td><td>Basa</td></tr><tr valign="top"><td scope="row">bat</td><td> </td><td>Baltic (Other)</td></tr><tr valign="top"><td scope="row">bej</td><td> </td><td>Beja</td></tr><tr valign="top"><td scope="row">bel</td><td>be</td><td>Belarusian</td></tr><tr valign="top"><td scope="row">bem</td><td> </td><td>Bemba</td></tr><tr valign="top"><td scope="row">ben</td><td>bn</td><td>Bengali</td></tr><tr valign="top"><td scope="row">ber</td><td> </td><td>Berber (Other)</td></tr><tr valign="top"><td scope="row">bho</td><td> </td><td>Bhojpuri</td></tr><tr valign="top"><td scope="row">bih</td><td>bh</td></tr><tr valign="top"><td scope="row">bik</td><td> </td><td>Bikol</td></tr><tr valign="top"><td scope="row">bin</td><td> </td><td>Bini</td></tr><tr valign="top"><td scope="row">bis</td><td>bi</td><td>Bislama</td></tr><tr valign="top"><td scope="row">bla</td><td> </td><td>Siksika</td></tr><tr valign="top"><td scope="row">bnt</td><td> </td><td>Bantu (Other)</td></tr><tr valign="top"><td scope="row">tib/bod</td><td>bo</td><td>Tibetan</td></tr><tr valign="top"><td scope="row">bos</td><td>bs</td><td>Bosnian</td></tr><tr valign="top"><td scope="row">bra</td><td> </td><td>Braj</td></tr><tr valign="top"><td scope="row">bre</td><td>br</td><td>Breton</td></tr><tr valign="top"><td scope="row">btk</td><td> </td><td>Batak (Indonesia)</td></tr><tr valign="top"><td scope="row">bua</td><td> </td><td>Buriat</td></tr><tr valign="top"><td scope="row">bug</td><td> </td><td>Buginese</td></tr><tr valign="top"><td scope="row">bul</td><td>bg</td><td>Bulgarian</td></tr><tr valign="top"><td scope="row">bur/mya</td><td>my</td><td>Burmese</td></tr><tr valign="top"><td scope="row">byn</td><td> </td><td>Blin; Bilin</td></tr><tr valign="top"><td scope="row">cad</td><td> </td><td>Caddo</td></tr><tr valign="top"><td scope="row">cai</td><td> </td><td>Central American Indian (Other)</td></tr><tr valign="top"><td scope="row">car</td><td> </td><td>Carib</td></tr><tr valign="top"><td scope="row">cat</td><td>ca</td><td>Catalan; Valencian</td></tr><tr valign="top"><td scope="row">cau</td><td> </td><td>Caucasian (Other)</td></tr><tr valign="top"><td scope="row">ceb</td><td> </td><td>Cebuano</td></tr><tr valign="top"><td scope="row">cel</td><td> </td><td>Celtic (Other)</td></tr><tr valign="top"><td scope="row">cze/ces</td><td>cs</td><td>Czech</td></tr><tr valign="top"><td scope="row">cha</td><td>ch</td><td>Chamorro</td></tr><tr valign="top"><td scope="row">chb</td><td> </td><td>Chibcha</td></tr><tr valign="top"><td scope="row">che</td><td>ce</td><td>Chechen</td></tr><tr valign="top"><td scope="row">chg</td><td> </td><td>Chagatai</td></tr><tr valign="top"><td scope="row">chi/zho</td><td>zh</td><td>Chinese</td></tr><tr valign="top"><td scope="row">chk</td><td> </td><td>Chuukese</td></tr><tr valign="top"><td scope="row">chm</td><td> </td><td>Mari</td></tr><tr valign="top"><td scope="row">chn</td><td> </td><td>Chinook jargon</td></tr><tr valign="top"><td scope="row">cho</td><td> </td><td>Choctaw</td></tr><tr valign="top"><td scope="row">chp</td><td> </td><td>Chipewyan</td></tr><tr valign="top"><td scope="row">chr</td><td> </td><td>Cherokee</td></tr><tr valign="top"><td scope="row">chu</td><td>cu</td><td>Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic</td></tr><tr valign="top"><td scope="row">chv</td><td>cv</td><td>Chuvash</td></tr><tr valign="top"><td scope="row">chy</td><td> </td><td>Cheyenne</td></tr><tr valign="top"><td scope="row">cmc</td><td> </td><td>Chamic languages</td></tr><tr valign="top"><td scope="row">cop</td><td> </td><td>Coptic</td></tr><tr valign="top"><td scope="row">cor</td><td>kw</td><td>Cornish</td></tr><tr valign="top"><td scope="row">cos</td><td>co</td><td>Corsican</td></tr><tr valign="top"><td scope="row">cpe</td><td> </td><td>Creoles and pidgins, English based (Other)</td></tr><tr valign="top"><td scope="row">cpf</td><td> </td><td>Creoles and pidgins, French-based (Other)</td></tr><tr valign="top"><td scope="row">cpp</td><td> </td><td>Creoles and pidgins, Portuguese-based (Other)</td></tr><tr valign="top"><td scope="row">cre</td><td>cr</td><td>Cree</td></tr><tr valign="top"><td scope="row">crh</td><td> </td><td>Crimean Tatar; Crimean Turkish</td></tr><tr valign="top"><td scope="row">crp</td><td> </td><td>Creoles and pidgins (Other)</td></tr><tr valign="top"><td scope="row">csb</td><td> </td><td>Kashubian</td></tr><tr valign="top"><td scope="row">cus</td><td> </td><td>Cushitic (Other)</td></tr><tr valign="top"><td scope="row">wel/cym</td><td>cy</td><td>Welsh</td></tr><tr valign="top"><td scope="row">cze/ces</td><td>cs</td><td>Czech</td></tr><tr valign="top"><td scope="row">dak</td><td> </td><td>Dakota</td></tr><tr valign="top"><td scope="row">dan</td><td>da</td><td>Danish</td></tr><tr valign="top"><td scope="row">dar</td><td> </td><td>Dargwa</td></tr><tr valign="top"><td scope="row">day</td><td> </td><td>Dayak</td></tr><tr valign="top"><td scope="row">del</td><td> </td><td>Delaware</td></tr><tr valign="top"><td scope="row">den</td><td> </td><td>Slave (Athapascan)</td></tr><tr valign="top"><td scope="row">ger/deu</td><td>de</td><td>German</td></tr><tr valign="top"><td scope="row">dgr</td><td> </td><td>Dogrib</td></tr><tr valign="top"><td scope="row">din</td><td> </td><td>Dinka</td></tr><tr valign="top"><td scope="row">div</td><td>dv</td><td>Divehi; Dhivehi; Maldivian</td></tr><tr valign="top"><td scope="row">doi</td><td> </td><td>Dogri</td></tr><tr valign="top"><td scope="row">dra</td><td> </td><td>Dravidian (Other)</td></tr><tr valign="top"><td scope="row">dsb</td><td> </td><td>Lower Sorbian</td></tr><tr valign="top"><td scope="row">dua</td><td> </td><td>Duala</td></tr><tr valign="top"><td scope="row">dum</td><td> </td><td>Dutch, Middle (ca.1050-1350)</td></tr><tr valign="top"><td scope="row">dut/nld</td><td>nl</td><td>Dutch; Flemish</td></tr><tr valign="top"><td scope="row">dyu</td><td> </td><td>Dyula</td></tr><tr valign="top"><td scope="row">dzo</td><td>dz</td><td>Dzongkha</td></tr><tr valign="top"><td scope="row">efi</td><td> </td><td>Efik</td></tr><tr valign="top"><td scope="row">egy</td><td> </td><td>Egyptian (Ancient)</td></tr><tr valign="top"><td scope="row">eka</td><td> </td><td>Ekajuk</td></tr><tr valign="top"><td scope="row">gre/ell</td><td>el</td><td>Greek, Modern (1453-)</td></tr><tr valign="top"><td scope="row">elx</td><td> </td><td>Elamite</td></tr><tr valign="top"><td scope="row">eng</td><td>en</td><td>English</td></tr><tr valign="top"><td scope="row">enm</td><td> </td><td>English, Middle (1100-1500)</td></tr><tr valign="top"><td scope="row">epo</td><td>eo</td><td>Esperanto</td></tr><tr valign="top"><td scope="row">est</td><td>et</td><td>Estonian</td></tr><tr valign="top"><td scope="row">baq/eus</td><td>eu</td><td>Basque</td></tr><tr valign="top"><td scope="row">ewe</td></tr></tbody></table>




 


8. mplayer音視頻同步原理

  
mplayer播放時的大循環過程爲:
while(!mpctx->eof){

  fill_audio_out_buffers();//音頻stream的讀取,解碼,播放
  update_video(&blit_);//視頻stream的讀取,解碼,過濾處理
  sleep_until_update(&time_, &aq_sleep_time);//計算延遲時間並睡眠等待
  mpctx->video_out->flip_page();//視頻的播放
  adjust_sync_and_print_status(_time_remaining, time_);//根據音視頻的PTS做同步矯正處理

}

音視頻同步方法爲
1)音頻播放playsize = mpctx->audio_out->play(sh_audio->a_out_buffer, playsize,  playflags);  後,根據數據大小算出時間並累計
mpctx->delay += playback_speed*playsize/(double)ao_data.bps;
2)視頻解碼前,用累計延遲時間剪掉本禎視頻的時間mpctx->delay -= _time;
3)計算聲音延遲時間*time_ = delay - mpctx->delay / playback_speed;
其中float delay = mpctx->audio_out->get_delay();爲距當前聲音OUTPUT BUF裏數據被全部播放完爲止所需的時間。
4)播放視頻同步完成,所以視頻的播放是完全根據聲卡最後的數據輸出來同步的。
5)計算出當前音視頻PTS差double AV_delay = a_pts - audio_delay - v_pts;再算出矯正值x = (AV_delay + timing_error * playback_speed) * 0.1f;最後把矯正的時間加到延遲累計中mpctx->delay+=x;。

 

 

9.  修改TCPMP界面

  實際上,改寫TCPMP的界面非常簡單。你可以看到TCPMP目錄下面有一個Sample的目錄,其中的sample_win32.c實現了對Player的調用,Player就是指播放器。它是一個沒有界面的程序,但是它能很正常的播放視頻,操作完全是Player指針。你可以通過這個Player指針來操作上一曲,下一曲,暫停,播放,停止等操作。界面你可以完全自己配。Player和你的界面交互也可以通過設置Notify來實現。這麼說可能還有點迷糊。下面舉個例子看怎麼調用Player指針 
  Player->Set(Player, PLAYER_LIST_URL+0, URL, sizeof(URL));  //這個就是設置播放的文件 
  Player->Set(Player, PLAYER_FULLSCREEN, &Bool, sizeof(Bool)); // 這個就是設置全屏播放 

  TCPMP是一個模塊化很強的程序,node是每個模塊的聯繫紐帶。每個node都會有一些設置屬性通過函數Set和Get來進行屬性的操作。Player就是一個node,它當然也有很多屬性,出來上面的PLAYER_LIST_URL,PLAYER_FULLSCREEN, 在common目錄下的Player.h文件中,你可以看到它定義了一系列的PLAYER_×××,在仔細看看它的註釋,這些就是Player的所有操作。你只要知道了Player指針,你可以做播放器裏的任何它這邊已經定義好的操作。

 

10. windows mobile使用libmad解碼mp3

 

最近由於項目的需要,需要在windows mobile上實現對mp3的流媒體播放,於是仔細研究了一段時間的libmad這個著名的開源解碼庫。libmad本來是個linux下的解碼庫,但是現在已經有人將之移植到windows mobile上,參見:http://blog.chinaunix.net/u/26691/showart_438320.html

利用libmad來播放mp3的思路很簡單:開啓兩個線程,其中一個線程用來解碼,一個線程用來播放,中間開一個數據隊列用來連接這兩個線程。解碼線程將解碼好的數據放進數據隊列,播放線程從數據隊列中取解碼好的數據用來播放。其中播放的時候用到了雙緩衝技術。

 

以下是我自己的程序在調用libmad庫來解碼時的大致代碼:

解碼的線程函數:

 

DWORD WINAPI CMediaPlay::Decode(void * ptemp)

{

      CMediaPlay *p = (CMediaPlay*)ptemp;

      TCHAR *szMusicName = _T("//My Documents//My Music//test.mp3");

      CFile file;

      if (!file.Open(szMusicName,CFile::modeRead))

      {

           TRACE(_T("Can't open the file !"));

           return 0;

      }

      unsigned long iNumReturn = file.Read(p->szMusicBuf,MUSICBUF);

// 以下三個函數用來初始化跟libmad庫,解碼完成後需要調用相應的finish函數釋放資源

      mad_stream_init(p->pm_stream);

      mad_frame_init(p->pm_frame);

      mad_timer_reset(&(p->Timer));

      mad_synth_init(p->pm_synth);

 

//將讀到的緩衝區跟libmad庫中的mad_stream對象關聯起來,由於我這是測試代碼,mp3

//文件很小,所以我是一次性全部讀進緩衝區,需要多次讀文件的可以去參看madlld這個基於//libmad代碼。

      mad_stream_buffer(p->pm_stream, p->szMusicBuf, iNumReturn);

//開始解碼

      for (;;)

      {

           while (mad_frame_decode(p->pm_frame, p->pm_stream)==-1)

           {

                 if (MAD_ERROR_BUFLEN == p->pm_stream->error)

                 {

                      bDecode = TRUE;

                      goto decodeEnd;

                 }

           }

//同志們呀,第一幀要丟掉呀!原因不明,我每次解出來後播放效果總是不理想,總是有噪

//音,不明原因,直到昨天仔細的看了下tcpmp的代碼後才發現tcpmp丟掉了第一幀數據。。

if (bFirstFrame)

           {

                 bFirstFrame--;

                 continue;

           }

 

           mad_timer_add(&(p->Timer),p->pm_frame->header.duration);

// 解碼輸出

           mad_synth_frame(p->pm_synth, p->pm_frame);

           DWORD length = p->pm_synth->pcm.channels == 2?p->pm_synth->pcm.length*sizeof(signed short)*2:

                                                                                                           p->pm_synth->pcm.length*sizeof(signed short);

           if (!p->outBuf)

           {

                 p->outBuf = new unsigned char[length];

           }

           unsigned char *OutputPtr = p->outBuf;

           for(int i=0;i<p->pm_synth->pcm.length;i++)

           {

                 signed short Sample;

      //下面的函數是爲了把解碼出來的32位數據轉成16位數據,直接從madlld扒出來滴

                 Sample=MadFixedToSshort(p->pm_synth->pcm.samples[0]);

                 *(OutputPtr++)=Sample&0xff;

                 *(OutputPtr++)=Sample>>8; 

                 if (p->pm_synth->pcm.channels == 2)

                 {

                      Sample=MadFixedToSshort(p->pm_synth->pcm.samples[1]);

                      *(OutputPtr++)=Sample&0xff;

                      *(OutputPtr++)=Sample>>8;

                 }

           }

      //將解碼完成的數據放進數據隊列供播放線程讀。

           SaveData(p->outBuf,length);

           ResumeThread(p->hThread2);    }

      return 0;

}

 

播放線程:

雙緩衝播放PCM音頻,這裏講得很詳細,參看:http://blog.csdn.net/pknife/archive/2008/05/21/2467581.aspx

 

 

 

11.  MP4文件格式

 

[mp4文件格式]獲取mp4文件信息1 - 計算電影長度 
 
方法1

從mvhd - movie header atom中找到time scale和duration,duration除以time scale即是整部電影的長度。

time scale相當於定義了標準的1秒在這部電影裏面的刻度是多少。

例如audio track的time scale = 8000, duration = 560128,所以總長度是70.016,video track的time scale = 600, duration = 42000,所以總長度是70

方法2

首先計算出共有多少個幀,也就是sample(從sample size atoms中得到),然後

整部電影的duration = 每個幀的duration之和(從Time-to-sample atoms中得出)

例如audio track共有547個sample,每個sample的長度是1024,則總duration是560128,電影長度是70.016;video track共有1050個sample,每個sample的長度是40,則總duration是42000,電影長度是70

[mp4文件格式]獲取mp4文件信息2 - 計算電影圖像寬度和高度

從tkhd – track header atom中找到寬度和高度即是。

 
[mp4文件格式]獲取mp4文件信息3 - 計算電影聲音採樣頻率 
 
從tkhd – track header atom中找出audio track的time scale即是聲音的採樣頻率。

[mp4文件格式]獲取mp4文件信息6 - 查找sample

當播放一部電影或者一個track的時候,對應的media handler必須能夠正確的解析數據流,對一定的時間獲取對應的媒體數據。如果是視頻媒體, media handler可能會解析多個atom,才能找到給定時間的sample的大小和位置。具體步驟如下:

1.確定時間,相對於媒體時間座標系統

2.檢查time-to-sample atom來確定給定時間的sample序號。

3.檢查sample-to-chunk atom來發現對應該sample的chunk。

4.從chunk offset atom中提取該trunk的偏移量。

5.利用sample size atom找到sample在trunk內的偏移量和sample的大小。

 

例如,如果要找第1秒的視頻數據,過程如下:

1.  第1秒的視頻數據相對於此電影的時間爲600

2.  檢查time-to-sample atom,得出每個sample的duration是40,從而得出需要尋找第600/40 = 15 + 1 = 16個sample

3.  檢查sample-to-chunk atom,得到該sample屬於第5個chunk的第一個sample,該chunk共有4個sample

4.  檢查chunk offset atom找到第5個trunk的偏移量是20472

5.  由於第16個sample是第5個trunk的第一個sample,所以不用檢查sample size atom,trunk的偏移量即是該sample的偏移量20472。如果是這個trunk的第二個sample,則從sample size atom中找到該trunk的前一個sample的大小,然後加上偏移量即可得到實際位置。

6.  得到位置後,即可取出相應數據進行解碼,播放


[mp4文件格式]獲取mp4文件信息7 - 查找關鍵幀

查找過程與查找sample的過程非常類似,只是需要利用sync sample atom來確定key frame的sample序號

確定給定時間的sample序號 
檢查sync sample atom來發現這個sample序號之後的key frame 
檢查sample-to-chunk atom來發現對應該sample的chunk 
從chunk offset atom中提取該trunk的偏移量 
利用sample size atom找到sample在trunk內的偏移量和sample的大小

 

 

 

12. Configuring AAC codec

 

In some cases, configuring AAC or eAAC+ codecs are required (e.g. when raw AAC streams are decoded, probably read from an mp4 file).
See also Forum Nokia Technical Library about configuring eAAC+ decoder: [1]

AAC codec UIDs
eAAC+ Decoder UID : 0x10207AA9
AAC Decoder UID: 0x101FAF81
第一個版本:(http://wiki.forum.nokia.com/index.php/Configuring_AAC_codec)
RArray<TInt> configParams;
configParams.Append(inputSampleRate);  // Input sample rate
configParams.Append(numChannels);      // Number of input channels [1, 2]
configParams.Append(profileType);      // AAC Input Profile [1 - LC, 3 - LTP]
configParams.Append(frameLength);      // Input Frame Len [1024, 960]
configParams.Append(downMixing);       // AAC Down Mixing [0 - none, 1 - mono, 2 - stereo]
configParams.Append(outputChannels);   // AAC output channels [0 - none, 1 - 1, 2 - 2]
configParams.Append(decimationFactor); // AAC decimation factor [0 - none, 2 - decimation by 2, 4 - decimation by 4]
configParams.Append(aacConcealment);   // AAC concealment [0 - none, 1 - basic]
configParams.Append(bitsPerSample);    // Sample resolution - [16 - 16-bit resolution]
configParams.Append(sampleRateConv);   // Sample Rate Conversion 0 : none
利用這個版本,配置AAC解碼器的時候成功,但是在解碼的時候就會出現system error(-1)
第二個版本(http://discussion.forum.nokia.co ... ight=aac#post471332)
eAAC+ Decoder UID :     0x10207aa9
AAC Decoder UID:        0x101faf81
[0] Fs:輸入採樣率<必填>
AAC解碼器所需的採樣率。
[1] 輸入通道<必填>
eAAC+解碼器可以用來判定輸出通道的數目(1個或2個)
eAAC+解碼器可以用來複制單聲道到2個聲道中,或者混音雙聲道爲單聲道。
(注意:這個參數一般都指明輸入聲道數的,但對eAAC+來說,卻要指定爲輸出聲道數)
[2] Profile(1或3)<必填>
1 = AAC-LC
3 = AAC-LTP
[3] 單位爲byte的輸出幀大小
如:將4096設置爲每幀1024採樣率的立體聲AAC-LC的幀大小。
注意如果指定了SBR並且FOut=2*Fs,那麼輸出採樣率數目應設爲8192。
(這並非是用戶配置選項,解碼器將忽略改值)
[4] 每幀的採樣率(1024)<舊參數>
只有1024才能被解碼器支持。
[5] 輸入採樣率(和[0]一樣)<必填>
[6] 不使用<舊參數>
[7] 過採樣模式(0, 1) <必填>
0 正常模式
1 設置爲Fout = Fs 這種情況下Fout將等於2*Fs(SBR開啓,並且Fs <= 24kHz)
Decoder is specified for level 3 support of AAC+ (max output sample rate is 48kHz
[8] 採樣大小(16) <必填>
每個採樣的比特數
[9] Fout: 輸出採樣率(0, 8000, 16000, FOut<推薦>)
調用3gp過採樣到8kHz或16kHz
0可以用來指明不執行3gp 8/16重新採用工具
[10] 擴展對象類型(5或6);如果Profile = 1纔能有效(必填)
5 = SBR (AAC-LC + SBR)
6 = PS  (AAC-LC + SBR + PS)
13.移動wap網關的一些限制說明:
1、大小限制:移動wap網關對於過大的數據會做攔截,按照經驗值,一般300K比較保險,
大於這個數據,建議採用分段下載的方式來解決;
2、HTTP返回值問題:部分地區的wap網關對HTTP返回值似乎也有限制,比如我們曾經測試過
400、500的返回值,移動網關是返回它自己的一個出錯頁面,所以在設計穿透cmwap/cmnet的協議
時,最好不用基於這個HTTP的響應值來承載出錯的協議,而是通過Content來承載;
3、Range問題:在POST方法下,Range的範圍如果大於300K,似乎也不大穩定,有可能被攔截,
所以需要慎重使用這個方法
14. MTK平臺用Socket實現HTTP請求總結

公司做了一個小型的wap瀏覽器的項目,其中涉及到用socket的實現http請求的方法,由於網上相關資料比較少,尤其是詳細的資料比較少,所以走了不少彎路。在此僅從實現的角度說明MTK平臺用Socket實現HTTP的方法,希望能給後來者一些微小的幫助。

 

一、MTK平臺Socket聯網過程
熟悉PC機編程的人都知道,Socket編程接口分兩套:TCP和UDP;TCP和UDP中又有服務器端和客戶端的概念,這裏講的是TCP的客戶端編程接口。

MTK平臺中Socket創建步驟:

         1、soc_create()  創建Socket;

         2、soc_setsockopt  設置Socket爲非阻塞模式;

         3、soc_setsockopt  設置Socket選項爲連接,讀,寫,關閉;不清楚爲什麼要連續設置兩次,如有高人路過,請指點;

        4、如果是CMNET聯網並且請求中用到了英文域名還需要解析域名soc_gethostbyname,除非使用ip作爲域名,解析出來的IP作爲我們建立連接的目標IP;如果是CMWAP聯網,直接跳到第5步,直接連接移動或聯通的網關:10.0.0.172:80;

        5、soc_connect與服務器建立連接;

        6、soc_send    發送請求;

        7、soc_recv     接收服務器返回的數據;

        8、soc_close    關閉Socket;

        9、如果需要關閉數據賬戶soc_close_nwk_account

二、CMNET,CMWAP方式下的HTTP請求內容格式
HTTP請求格式:

GET方法

MTK模擬器中wap瀏覽器發送的請求內容

“GET /go_13596557 HTTP/1.1

Host: kong.net

User-Agent: SQH_D480B_01/LB19504/WAP2.0 Profile

Accept:  application/vnd.wap.wmlc, **  //(想當長,省去後面部分)

Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, GB2312, windows-1252, us-ascii

Accept-Language: zh-tw, zh-cn, en

Cookie: JSESSIONID=aAQP0FIXp3z7

Connection: Keep-Alive

 

 

POST方法

對一些需要向服務器傳入參數的請求,按名稱搜索等請求。還以空中網天氣查詢爲例,之中的其他城市天氣查詢,輸入其他城市名稱或電話區號查詢:

 

“POST /weather/search.jsp?setcity=1 HTTP/1.1

Host: kong.net

User-Agent: SQH_D480B_01/LB19504/WAP2.0 Profile

Accept: application/vnd.wap.wmlc, */*  //(想當長,省去後面部分)

Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, GB2312, windows-1252, us-ascii

Accept-Language: zh-tw, zh-cn, en

Content-Type: application/x-www-form-urlencoded; charset=utf-8

Cookie: KONG_ACCESS=AWYZhg==; JSESSIONID=a91MDc6qoMYf

Connection: Keep-Alive

Content-Length: 46//get方法沒有這一項

 

××××××//傳給服務器46字節長的數據(參數)

當然如果是CMWAP聯網方式也要和上述的GET方法一樣設置Host和X-Online-Host項,Host:10.0.0.172

X-Online-Host: kong.net

以上的內容,可以在調試狀態下運行模擬器的wap瀏覽器,在soc_send方法處插入斷點觀察。

 

HTTP的其他方法,由於在應用中沒有用到,在這裏不做介紹。

三、CMNET,CMWAP連接差別
1、GPRS賬戶:

與pc機上的socket客戶端接口不同,手機客戶端在soc_create,soc_gethostbyname接口中都多了參數nwt_acount_id,只的是一般在“網絡服務”->“數據賬戶”->“GPRS”下的GPRS數據賬戶id,一般起始的一個賬戶id是10,往下遞增1,在建立連接過程中,如果是CMWAP方式聯網,soc_create,soc_gethostbyname接口就要設置接入點爲CMWAP的賬戶id,CMNET就要設置接入點爲CMNET的賬戶。

 

2、目標服務器:

還以空中網的天氣服務爲例,CMNET情況下,soc_connect需要連接”221.179.172.2”這個ip,如果請求的url爲”http://kong.net/weather/home.jsp”  ,還需要調用soc_gethostbyname接口去解析域名;

如果是CMWAP方式聯網,soc_connect只需要連接移動或聯動的網關”10.0.0.172:80”。

 

3、HTTP請求內容格式(或稱報文):

如第二節所述。

四、SIM1還是SIM2聯網
      SIM1還是SIM2聯網,MTK平臺是通過創建socket時傳入的nwt_acount_id區分的,如果是SIM1上網,賬號就是指的是一般在“網絡服務”->“數據賬戶”->“GPRS”下的對應的GPRS數據賬戶id;如果是SIM2,通過在四字節的賬戶id其他字節設置掩碼來區分。

設置接口比如07B平臺的always_ask_encode_data_account_id,6235_08A的cbm_encode_data_account_id接口。不同平臺可能略有差別。

五、聯通卡還是移動卡?
參考其他Socket聯網代碼中有的以接入點是否爲”uniwap”來判斷是不是聯通的代理上網,但是通過實驗,即使在聯通卡時連接移動的”cmwap”賬戶,也是可以正常聯網的。不知道設計“GPRS數據賬戶”的最初意圖是什麼?通過apn來區分同一內部ip地址網關不同的公網ip嗎?如有高人路過,請指點;

 

六、HTTP1.1與Transfer-Encoding 爲chunked的編碼方式
      發送一個請求後,如果服務器返回的消息頭內容包括“Transfer-Encoding: chunked”那麼他的傳輸編碼爲“chunked”類型。這種傳輸類型的數據體內容格式是這樣:

 

[16進制數字字符串 1到4個字節 len]/r/n

[len 長的數據體]/r/n

[16進制數字字符串 1到4個字節 len]/r/n

[len 長的數據體]/r/n

[16進制數字字符串 1到4個字節 len == 0]/r/n/r/n

 

其中,長度len是16進制的數字,表示本段數據體的長度(字節數),回車換行後,就是這一段數據真實內容,這就是一段數據體的格式,一段接一段;直到數據體長度爲0的數據段出現,緊接着兩個回車換行,標識本次請求的數據均已接收完畢。不過socket可以根據soc_recv返回值等於0來判斷接收數據結束。如果收到的是這個編碼類型的內容,需要對接收到的數據進行處理。

 

七、MTK平臺的S8類型的誤導
     MTK平臺定義的兩個數據類型U8和S8,一看名稱我們可能會以爲是unsigned char和signed char,但事實並非如此,

typedef char         S8;

typedef unsigned char  U8;

    MTK平臺的char默認也是unsigned char類型的,soc_gethostbyname返回值類型是kal_int8(typedef signed char  kal_int8;),如果S8或平臺的char類型是有符號的字符型,那麼,kal_int8和S8應該是等價的,但用S8類型變量作爲soc_gethostbyname的返回值時,經常返回254導致域名不會被正常解析,其實應該返回SOC_WOULDBLOCK(-2),應該是阻塞碼,將soc_gethostbyname返回值類型改爲kal_int8後,就能正常處理域名解析了。這證明平臺的S8類型及char類型默認是無符號的。

八、不理解的鏈接錯誤?
       在添加連接超時功能時用到了gui_start_timer和gui_cancel_timer時,沒有加入#include "gui.h"時,出現以下鏈接錯誤:

Error: L6286E: Value(0x818153e) out of range(-0x400000 - 0x3fffff) for relocation #13 (wrt symbol gui_cancel_timer) in Socket.obj(i. SocDinit)

加上#include "gui.h"時,就沒有這個問題,如果程序找不到這個符號,應該是個編譯錯誤,在此爲什麼是個鏈接錯誤。

 

查了一下arm的幫助文檔:http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka3553.html

依然不明白,如果高手路過,請深入指教一下原因。


發佈了20 篇原創文章 · 獲贊 4 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章