不断更新,欢迎交流和转载。
目录:
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的基本概念都可以在wikipedia和Audio 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
mdat box 存放了音频视频和其他的数据,一般的文件至少有2个mdatbox, 一个用于音频,一个用于视频, 通常还会有一些文本信息也放在mdatbox中,各种信息的顺序不固定。 如果只是存放音乐一个mdat就够用了。
你可能会问这么多box 都叫mdat我怎么知道音频放哪里视频放哪里呢?别着急这些相关信息都放在moovbox里面
影片包:(moov box:Movie Box:)是一个3gp文件中最复杂最重要的文件。
看到这里你可能会问"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
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;
然后就是采样包列表.
采样包有很多具体的实现.如下:
附录:
ISO-639-2/T language code
ISO 639-2 Code | ISO 639-1 Code | English name of Language |
---|---|---|
aar | aa | Afar |
abk | ab | Abkhazian |
ace | Achinese | |
ach | Acoli | |
ada | Adangme | |
ady | Adyghe; Adygei | |
afa | Afro-Asiatic (Other) | |
afh | Afrihili | |
afr | af | Afrikaans |
ain | Ainu | |
aka | ak | Akan |
akk | Akkadian | |
alb/sqi | sq | Albanian |
ale | Aleut | |
alg | Algonquian languages | |
alt | Southern Altai | |
amh | am | Amharic |
ang | English, Old (ca.450-1100) | |
anp | Angika | |
apa | Apache languages | |
ara | ar | Arabic |
arc | Aramaic | |
arg | an | Aragonese |
arm/hye | hy | Armenian |
arn | Araucanian | |
arp | Arapaho | |
art | Artificial (Other) | |
arw | Arawak | |
asm | as | Assamese |
ast | Asturian; Bable | |
ath | Athapascan languages | |
aus | Australian languages | |
ava | av | Avaric |
ave | ae | Avestan |
awa | Awadhi | |
aym | ay | Aymara |
aze | az | Azerbaijani |
bad | Banda | |
bai | Bamileke languages | |
bak | ba | Bashkir |
bal | Baluchi | |
bam | bm | Bambara |
ban | Balinese | |
baq/eus | eu | Basque |
bas | Basa | |
bat | Baltic (Other) | |
bej | Beja | |
bel | be | Belarusian |
bem | Bemba | |
ben | bn | Bengali |
ber | Berber (Other) | |
bho | Bhojpuri | |
bih | bh | |
bik | Bikol | |
bin | Bini | |
bis | bi | Bislama |
bla | Siksika | |
bnt | Bantu (Other) | |
tib/bod | bo | Tibetan |
bos | bs | Bosnian |
bra | Braj | |
bre | br | Breton |
btk | Batak (Indonesia) | |
bua | Buriat | |
bug | Buginese | |
bul | bg | Bulgarian |
bur/mya | my | Burmese |
byn | Blin; Bilin | |
cad | Caddo | |
cai | Central American Indian (Other) | |
car | Carib | |
cat | ca | Catalan; Valencian |
cau | Caucasian (Other) | |
ceb | Cebuano | |
cel | Celtic (Other) | |
cze/ces | cs | Czech |
cha | ch | Chamorro |
chb | Chibcha | |
che | ce | Chechen |
chg | Chagatai | |
chi/zho | zh | Chinese |
chk | Chuukese | |
chm | Mari | |
chn | Chinook jargon | |
cho | Choctaw | |
chp | Chipewyan | |
chr | Cherokee | |
chu | cu | Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic |
chv | cv | Chuvash |
chy | Cheyenne | |
cmc | Chamic languages | |
cop | Coptic | |
cor | kw | Cornish |
cos | co | Corsican |
cpe | Creoles and pidgins, English based (Other) | |
cpf | Creoles and pidgins, French-based (Other) | |
cpp | Creoles and pidgins, Portuguese-based (Other) | |
cre | cr | Cree |
crh | Crimean Tatar; Crimean Turkish | |
crp | Creoles and pidgins (Other) | |
csb | Kashubian | |
cus | Cushitic (Other) | |
wel/cym | cy | Welsh |
cze/ces | cs | Czech |
dak | Dakota | |
dan | da | Danish |
dar | Dargwa | |
day | Dayak | |
del | Delaware | |
den | Slave (Athapascan) | |
ger/deu | de | German |
dgr | Dogrib | |
din | Dinka | |
div | dv | Divehi; Dhivehi; Maldivian |
doi | Dogri | |
dra | Dravidian (Other) | |
dsb | Lower Sorbian | |
dua | Duala | |
dum | Dutch, Middle (ca.1050-1350) | |
dut/nld | nl | Dutch; Flemish |
dyu | Dyula | |
dzo | dz | Dzongkha |
efi | Efik | |
egy | Egyptian (Ancient) | |
eka | Ekajuk | |
gre/ell | el | Greek, Modern (1453-) |
elx | Elamite | |
eng | en | English |
enm | English, Middle (1100-1500) | |
epo | eo | Esperanto |
est | et | Estonian |
baq/eus | eu | Basque |
ewe |
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
公司做了一个小型的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
依然不明白,如果高手路过,请深入指教一下原因。