Z控制H口的大,在窗口初始化Ӟ需要用到MINMAXINFOl构体?/p>
l构体:
参数说明Q?br /> ptMaxSizeQ?nbsp; 讄H口最大化时的宽度、高?br /> ptMaxPositionQ?nbsp;讄H口最大化时x坐标、y坐标
ptMinTrackSizeQ?nbsp;讄H口最宽度、高?br /> ptMaxTrackSizeQ设|窗口最大宽度、高?br />
实例Q?br />1、在H口cLON_WM_GETMINMAXINFO消息Q?br />2、在该函C改MINMAXINFOl构体数据:
录音播音实际需?/p>
1、随时终?/p>
2、录韛_ƈ非文Ӟ而是形成rtp发?/p>
3、播韛_ƈ非源于文Ӟ而是源于rtp
因此Waveform audio使用的buffer较小Q不断的装蝲/发?buffer,l止的时候Resetq且close.
大致如下调用的@?/p>
录音
waveInUnprepareHeader
waveInPrepareHeader
waveInAddBuffer
播音
waveOutUnprepareHeader
waveOutPrepareHeader
waveOutWrite
循环周期40msQ我采用的是回调函数。问题是有时候调用waveInReset/waveOutReset会Ş成死锁,调用waveInReset/waveOutReset的线E与回调函数所在的U程死锁在一块了?/p>
q方面网上有文章提到Q就是调用waveInReset/waveOutReset的同时调用了录音/播音循环调用的某个函C形成死锁。我再稍作解释下Q我们知道buffer满了或是调用Reset都会触发消息Q回调函数方式的话就是MM_WOM_DONE/MM_WIM_DATAQ,׃调用waveInReset/waveOutReset所在的U程Q与回调函数所在的U程不是一个线E,因此很容易撞车,也就是说Q你调用reset的时候,另一个线E正好在处理MM_WOM_DONE/MM_WIM_DATAQ于是就q样死锁了?/p>
先加上标?假设标记bReset:bool)QobReset为true;
标记作用如下
if(!bReset)
{
录音
waveInUnprepareHeader
waveInPrepareHeader
waveInAddBuffer
播音
waveOutUnprepareHeader
waveOutPrepareHeader
waveOutWrite
}
延时调用waveInReset/waveOutResetQg时时间长度以循环周期为妙Q我q个例子中也是采用40ms?/p>
当然也可以采用界保护?/p>
换一个角度去考虑问题Q之所以死锁,是因Z个线E冲H了的缘故,所以可以徏立一个线E?/p>
录音
waveInUnprepareHeader
waveInPrepareHeader
waveInAddBuffer
播音
waveOutUnprepareHeader
waveOutPrepareHeader
waveOutWrite
与waveInReset/waveOutReset都放到这个线E去处理Q自然不会发生死锁了?/p>
微Y的代码签名Y?SignCode.exe 的缺省的“典型”{cdQ就?#8220;从存储区选择”{证书Q同时由于微软的 Office 宏代码签名只支持同时包含了私钥和公钥?PFX 格式{证书Q也是直接“从存储区选择”{证书。在收到证书后请用户?Windows 的证书存储区导出备䆾{证书Q导出的证书格式?PFX 格式Q保好证书的密码?br />
下蝲Thawte代码{证书的中U根证书Q?br />https://search.thawte.com/support/ssl-digital-certificates/index?page=content&id=AR1382
下蝲VeriSign代码{证书的中U根证书Q?br />http://www.verisign.com/support/verisign-intermediate-ca/code-signing-intermediate/index.html
使用微Y?span class="Apple-converted-space"> SignCode.exe 可以对微Y的代码进行签名,如果您没有此文gQ您可以点击 q里下蝲?Signcode.exe 可以使用 DOS 命o行方式实现签名,我们推荐用户使用数字{向导方式Q简单方ѝ请注意Q如果您开发的ActiveX为IE加蝲,请先数字{每个CAB文g中的.dll?ocx{文Ӟ再把q些文g打包?cab文g后再数字{.cab文gQ以保所有IE加蝲w被IE验证和信任,否则会显C?#8220;未验?#8221;而可能媄响正常运行?br />
具体{向导q程如下Q?/p>
(1) q行 Signcode.exe Q要求您选择需要签名的文gQ支持:可执行文?(*.exe; *.dll; *.ocx) Q?Cabinet 打包文g (*.cab) 和目录文?(*.cat) Q如下图 1 所C?( 如: TestSign.cab) Q请注意Q如果签名的文g已经有数字签名,则会被新的签名覆?span class="Apple-converted-space">
(2) 点击“下一?#8221;后,如下?2 所C,会要求您选择“{cd”Q?直接点击“下一?#8221;卛_Q即选择~省?#8220;典型”{cdQ?span class="Apple-converted-space">
(3) 如下?3 所C,点击“从存储区选择”Q则会显C您的电脑证书存储区的所有证书,包括存储在电脑和 USB Key 中的所有数字证书,选择您的{证书卛_Q?span class="Apple-converted-space">
(4) 如下?4 所C,要求填写该签名代码的功能描述Q推荐一定要认真填写Q因为此信息会在最l用户下载此代码时显C,有助于最l用户了解此代码的功能以定是否下蝲安装。第一?#8220;描述”是指此代码的功能文字描述Q第二行“ Web 位置”则让最l用LL字描q来详细了解此代码的功能和用方法等?/p>
(5) 点击“下一?#8221;后,如下?9 所C,选中“时间戳d到数据中”Q请使用:
VeriSign 免费提供的时间戳URLQ?strong style="color: rgb(86,87,89); font-size: 12px">http://timestamp.verisign.com/scripts/timestamp.dll
旉x务非帔R要,d旉戛_Q即使您的代码签名证书已l过期,但由于您的代码是在证书有效期内签名的Q则旉x务保证了此代码仍然可信,最l用户仍然可以放心下载,使得即代码{证书已经q期Q您也无需重签已经{的代码?/p>
(6) 点击“下一?#8221;后,如下?6 所C,会提C已l完成数字签名向|点击“完成”完成了中文版代码签名证书的代码{?br />
在VOIP的音频算法中Q回韛_理已l成Z个关p通话质量的主要问题?/strong>
回声的生在IP|络主要有两U:
1.声学回声
2.电\回声
声学回声主要又分成以下几U?
a ) 直接回声:由扬声器产生的声xlQ何反直接进入麦克风
b ) 间接回声: 由扬声器发出的声音经q多ơ反后,再进入Mic
对于W二U回?拥有多\?时变性的特点.是比较难处理?
׃IP|络下的传输的gq较?而一般情况下,对于?如果声音延迟辑ֈ?0ms以上的话,那么回声׃来明?
一般来?VOIP中的声音延迟主要来自于几个方?
1. ~码延迟: 一般情况下~码法在声韛_~时都会产生延迟,我们采用的Speex来讲,延迟大概?0ms左右
2. 处理延迟, 装时g, ~冲时g{?br />3. 在IP|络中数据的传输q程也会照成延时.q由当前的网l状况决?
回声消除的模?
a) 建立q端声音模型,q行回声估计, 从采集的g减去估计?br />b) 声学模型
Speex是一套专门用于压~声音的?/span>,׃其专门针对声?/span>,所以压~声音的性能非常?/span>.Speex׃其压~性能,?/span>0.80版后的跨q_的性能,所以在|络声音的传输中有很大的价?/span>.但是需要注意的?/span>speex只能对声韌行压~?/span>,不支持音乐的压羃,如果你需要音乐的压羃你或讔R要用vorbis?/span>.
但是?/span>speex资料像其它大都数专用库一?/span>,q没有大多的中文资料.所以在q里我决定将里面最核心的编E技术翻译出?/span>.一来是l习l习自己译资料的能?/span>,二来是方便一些英语水q差的朋友.׃本h能力有限,有些感觉有出入或隄解的地方可以?/span>speex的官方网?/span>www.speex.org扑ֈ英文原版的说?/span>.
译的一些说?/span>:
1,对于一些专有名词如speex,api不过行翻?/span>
2,对于一些新概念译,以及其它的翻译过来也怼产生歧义的文?/span>,用中/׃U方式标?/span>:
?/span>:对话(speech),位采?/span>(bit_packing)
3,基本做到和英文原行对?/span>.及英文原文一?/span>,中文译q来也是一?/span>,使翻译后的文章和原文基本行行对照.
4,源代码不译,?/span>SpeexBits bits;
5,语言中的兛_字不?/span>,?/span>float
6,一此不是关健字但英文通常出现的词W一ơ以?/span>/英文格式l出,之后按具体情늻文或中文,?/span>:frame(?/span>),
7,对一些有自己译h感觉有歧义的地方,加斜U作标记
1,speex的介l??
2特征描述
q个章节展示?/span>speex的主要特?/span>,以衣一些关于对?/span>(speech)~码的一个概?/span>,以便
帮助我们更好的了解下一章节.
取样?span>(Sampling rate)
Speex主要是设计了三种不同的取L:8kHz,16kHz,32kHz.q些分别代表了窄?/span>(narrowband),
多频?/span>,声.
质量
Speex~码大都数时间是被一个范围ؓ0?/span>10的质量参数来控制 ?/span>.在一个比特率为常?/span>(CBR)的操作中,质量参数是一个整?/span>,而对于变动的比特?/span>(VBR)参数是一?/span>float;
复杂?/span>(变量)
?/span>speex,你可以将~码讄成允许的复杂?/span>.q由一个范围ؓ1?/span>10的整数来控制完成,像你用选项-1?/span>-9来控?/span>gzip?/span>bzip2的压~质?/span>.在通常的运用中,噪声U别的复杂度1是在1?/span>2dB之间,比复杂度10要高,但是CPU需要复杂度10大概5倍高行复杂度1.在实践中,最好的是设|在2?/span>4之间,管更高的设定通常有用,当编码一个非对话声音(non-speech sounds)?/span>DTMF语调(tones).
变L特率(VBR
Variable bit-rate (VBR) allows a codec to change its bit-rate dynamically to adapt to
变L特率(VBR)允许~码动态地改变它的波特率以适应声音~码?/span>”隑ֺ”.?/span>speex举例来说,
像元?/span>(vowel)和瞬间高?/span>(high-enenrg transients)需要个高的比特率来
取得一个不错的质量,
而摩擦音(fricative)可以被充分地用相对较的字节来进行编?/span>.
׃上面q个原因,VBR可以调节C个低的比特率却达C个同L质量,或者用
某个比特率达到更好的质量.管有上面这些优?/span>,但是VBR也有两个主要的缺?/span>.
首先,仅仅靠指定质量?/span>,q里没有一个关于最后^均比特率的保?/span>.(译者注:作者大概是惌没有什么明的Ҏ知道质量?/span>)此外,对一些即?/span>
通信,?/span>IP电话(VoIP)q种包含着最大的比特率的,必须把比特率设ؓ_低以适应
传输通道.
r
q_比特?span>(ABR)
q_比特率通过动态地调节VBR质量dC个确定的目标的比特率,从而解决了VBR中的一个问?/span>..因ؓ质量/比特率被x的调整了,整体质量会E稍低于?/span>VBR对一?/span>
讄得和目标q_比特率非常接q的质量数编码得到的l果.
声音生动性检?span>(VAD)
声音生动性检将会发觉音频正在被~码成对?/span>,静音,或背景噪?/span>.VADd?/span>VBRq行~码时暗中v作用,因此选项仅仅对一个不?/span>VBR的操作v作用.对于不是VBR的操作来?/span>,speex察觉Z个不属于对话的周?/span>,然后对它用够的字节重新生成景噪?/span>.不这叫做舒适的噪音生成(CNG).
不连l传?span>(DTX)
不连l传输是VAD/VBR操作的一个额外选项,当背景噪音一定时,它可以完整地传输.因ؓ在基于文件的操作?/span>,我们不能停止Ҏ件进行写?/span>,所以只?/span>5字节被这U所q用.(l?/span>250bps通信)
Perceptual enhancement
知觉增强
知觉增加是解码的一部分,它在打开的时候用来减由~码解码所产生的噪?/span>.在大都数
情况?/span>,知觉增强在客观上使声音离原始值更q?/span>(如果?/span>SNR),但是在最后它仍然听v来更?/span>(主观上的改进)
Algorithmic delay
q时法
每一个声音编码导致了在传输上的g?/span>.对于speex,q种延时{于frame的大加上一?/span>
数量的需要对每一帧进行的前瞻(”look-adhead”).
在窄宽操作中(8kHz),q时?/span>30ms,而对于多频率(2-44Hz),q时?/span>34ms.q些?/span>
不包?/span>CPU~码,解码帧的旉.
?span>speex~程(the libspeex api)
q个章节讲述了如何用speex apiq行~程.例子的源代友你也可以在附?/span>B中找?/span>
4.1 Encoding
4.1压羃
Z?/span>Speex压羃对话,你首先需要引用头文g:
#include <speex.h>
然后你需要定义一?/span>Speex的位采集(bit-packing)l构
SpeexBits bits;
and a Speex encoder state
以及定义一?/span>speex~码器状态量
void *enc_state;
上面定义的这栯初始?/span>:
The two are initialized by:
speex_bits_init(&bits);
enc_state = speex_encoder_init(&speex_nb_mode);
Z支持多频率的压羃,speex_nb_mode被sppex_wb_mode取代.在大都数
情况?/span>,你需要知道你用的模式(mode)的(frame)的大?/span>,你可以得到在frame_size变量里得到这?/span>:
speex_encoder_ctl(enc_state,SPEEX_GET_FRAME_SIZE,&frame_size);
一但初始化完毕,对于每一个输入:
speex_bits_reset(&bits);
speex_encode(enc_state, input_frame, &bits);
nbBytes = speex_bits_write(&bits, byte_ptr, MAX_NB_BYTES);
上面input_frame是一个指向对?/span>(speech)?/span>(frame)?/span>float指针(pointing);byte_ptr
是指向编码开始写的地方的char指针,MAX_NB_BYTES是能
写进byte_ptr而不会造成溢出的最大数.nbBytes是一个实际写?/span>btye_ptr的数,即编码的实际大小
在调?/span>speex_bits_write?/span>,可能你需要调?/span>speex_bits_nbytes(&bits)得到需要写?/span>(write)的字节大?/span>.
在你已经~码?/span>,释放所有的资源.
speex_bits_destroy(&bits);
speex_encoder_destroy(enc_state);
That’s about it for the encoder.
q就是关于编码的斚w.
附源代码的翻?/span>:
B Sample code
B 例程源代?/strong>
q个章节演示了一D는speex~码,解码对话(speech)的源代码.
可以如下?/span>api命o来编码ƈ解码一个文?/span>:
译者注:q里说的api命o是指unix的用”|”q行道写入d.?/span>windows下这样ƈ不能实现.
% sampleenc in_file.sw | sampledec out_file.sw
q里q两D代码都没有引用其它的头文g,q以16 比特?/span>(bits)q行~码
natural endianness).
B.1 sampleenc.c
Sameleenc 用一个未加工?/span>16比特?/span>(bits)文章,l它~码q生一?/span>speex ?/span>(steam)l标准输?/span>.注意已压~的?/span>speexenc/speexdec不和?/span>!
#include <speex.h>
#include <stdio.h>
/*帧的大小在这个例E中是一个固定的?/span>,但它q不是必这?/span>*/
#define FRAME_SIZE 160
int main(int argc, char **argv)
{
char *inFile;
FILE *fin;
short in[FRAME_SIZE];
float input[FRAME_SIZE];
char cbits[200];
int nbBytes;
/*保存~码的状?/span>*/
void *state;
/*保存字节因此他们可以?/span>speex常规d*/
SpeexBits bits;
int i, tmp;
//新徏一个新的编码状态在H宽(narrowband)模式?/span>
state = speex_encoder_init(&speex_nb_mode);
//讄质量?/span>8(15kbps)
tmp=8;
speex_encoder_ctl(state, SPEEX_SET_QUALITY, &tmp);
inFile = argv[1];
fin = fopen(inFile, "r");
//初始化结构他们保存数据
speex_bits_init(&bits);
while (1)
{
//d一?/span>16bits的声?/span>
fread(in, sizeof(short), FRAME_SIZE, fin);
if (feof(fin))
break;
//?/span>16bits的D{化ؓfloat,以便speex库可以在上面工作
for (i=0;i<FRAME_SIZE;i++)
input[i]=in[i];
//清空q个l构体里所有的字节,以便我们可以~码一个新的
speex_bits_reset(&bits);
//对q行~码
speex_encode(state, input, &bits);
//?/span>bits拯C个利用写出的char型数l?/span>
nbBytes = speex_bits_write(&bits, cbits, 200);
//首先写出帧的大小,q是sampledec文g需要的一个?/span>,但是你的应用E序中可能不一?/span>
fwrite(&nbBytes, sizeof(int), 1, stdout);
//写出压羃后的数组
fwrite(cbits, 1, nbBytes, stdout);
}
//释放~码器状态量
speex_encoder_destroy(state);
//释放bit_packingl构
speex_bits_destroy(&bits);
fclose(fin);
return 0;
}
在SpeexQ?a style="background-color: transparent; color: rgb(51,102,153); text-decoration: none; background-origin: initial; background-clip: initial" >www.speex.orgQ的最新版本中Q开始集成了回音消除的模块,而回x除一直是Voip之中亟待解决的主要问题?br />很多朋友和我说speex的aec模块的效能ƈ不好Q我们先来看一下speex的aec的api调用方式?/p>
/*
*创徏AEC对象
*/
SpeexEchoState *echo_state = speex_echo_state_init(frame_size, filter_length);
frame_size 的取值最好是一个编码的frame大小Q?在低带宽条g下,一般gq?0msQ而大ؓ160
filter_length,最好是戉K内反时间的1/3
? 一个房间的反射时g?00ms
那么q个filter_length最好是100ms(q个长度又被UCؓtail length).
而其中filter_length的设定是一个关键?/p>
/*
*执行AEC
*/
speex_echo_cancel(echo_state, input_frame, echo_frame, output_frame, residue);
其中:
input_frame: 是被声卡捕捉到的声?br />echo_frame: 是由扬声器播攑և的声?q个声音是需要从 input_frame中抵消的声音.
output_frame 是处理完以后输出的声?/p>
residue是一个可选参?如果不用可以将之设|ؓNULL, 也可以通过preprocessor 来控?/p>
问题的关键是 处理input和echo 之间的关p?
也就是说在捕捉到的信号和播放的信号之间的延迟必须_的小,才可以提高效?
writetosndcard(echo_frame, frame_size)
readfromsndcard(input_frame, frame_size)
speex_echo_cancel(echo_state, input_frame, echo_frame, output_frame, residue)
如果你想要尽可能的减信号中的回?那么可以residueq个参数讄为噪韛_?
我相信在大多数情况下Q都是因为声x捉和声音播放之间的同步问题没有处理好Q导致的音频质量下降?/p>
/*
*销毁和复位
*/
speex_echo_state_destroy(echo_state);
speex_echo_state_reset(echo_state);
不再复述了!
说明Q?br />据说在Speex的最新的1.2beta版本上,Speex提供了可选择的,化的APIQ来提高echo执行q程中的同步问题?/p>
AEC回声抑制法Q这个比较难Q目前可以用directsoundq行处理Q不q只能在xp下用,别的pȝ不支持!
目前gipsҎ法有出色的实现Qskype是使用的该引擎Q?br />要想自己实现Q恐怕很困难Q?br />
AEC 模块?Microsoft DirectSound 底层l构的一部分。该lg包括下列Ҏ和限制Q?/p>
AEC只在不超q?25×15×9 英尺的小戉K才会有效Q?span class="Apple-converted-space">
AEC只对单声道有效,当输出是多个通道的立体声的时候,只有一个通道能够h回L抉|的效果;
AEC不能抉|来自其它声音源的声音Q比如背景中攉机放出来的歌Ԍ
IDirectSoundFullDuplex8* DirectSoundFD;//
IDirectSoundCaptureBuffer8* DirectSoundCaptureBuf8;//捕捉~冲区接口指?br />IDirectSoundBuffer8* DirectSoundBuf8;//回放~冲区接口指?br />IDirectSoundBuffer8* pIUnkown;//回放~冲区接口指?/p>
extern "C" const GUID IID_IDirectSoundBuffer8 = {0x6825a449, 0x7524, 0x4d82,{ 0x92, 0x0f, 0x50, 0xe3, 0x6a, 0xb3, 0xab, 0x1e}};
extern "C" const GUID GUID_DSCFX_MS_NS = {0x11c5c73b, 0x66e9, 0x4ba1, {0xa0, 0xba, 0xe8, 0x14, 0xc6, 0xee, 0xd9, 0x2d}};
extern "C" const GUID GUID_DSCFX_CLASS_NS = {0xe07f903f, 0x62fd, 0x4e60, {0x8c, 0xdd, 0xde, 0xa7, 0x23, 0x66, 0x65, 0xb5}};
extern "C" const GUID GUID_DSCFX_MS_AEC = {0xcdebb919, 0x379a, 0x488a, {0x87, 0x65, 0xf5, 0x3c, 0xfd, 0x36, 0xde, 0x40}};
extern "C" const GUID GUID_DSCFX_CLASS_AEC = {0xBF963D80L, 0xC559, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
extern "C" const GUID DAlgorithm ={0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
//1.创徏及初始化DirectSound
WAVEFORMATEX WaveDataFormat={WAVE_FORMAT_PCM, 1,8000,16000,2,16, 0};
//回放~冲区。render buffer
DSBUFFERDESC desc;
memset(&desc, 0, sizeof(desc));
desc.dwSize = sizeof(desc);
desc.dwFlags = DSBCAPS_CTRLFX | DSBCAPS_GLOBALFOCUS;
desc.dwBufferBytes = 2000 * NUM_REC_NOTIFICATIONS;//待定
desc.dwReserved = 0;
desc.lpwfxFormat = &WaveDataFormat;
//捕捉~冲区AEC和NS效果?br />DSCEFFECTDESC efft[2];
memset(efft, 0, sizeof(efft));
//AEC效果
efft[0].dwSize = sizeof(efft[0]);
efft[0].dwFlags = DSCFX_LOCSOFTWARE;
efft[0].guidDSCFXClass = GUID_DSCFX_CLASS_AEC;
efft[0].guidDSCFXInstance = GUID_DSCFX_MS_AEC;
//NS效果
efft[1].dwSize = sizeof(efft[1]);
efft[1].dwFlags = DSCFX_LOCSOFTWARE;
efft[1].guidDSCFXClass = GUID_DSCFX_CLASS_NS;
efft[1].guidDSCFXInstance = GUID_DSCFX_MS_NS;
//捕捉~冲区。capture buffer
DSCBUFFERDESC cdesc;
memset(&cdesc, 0, sizeof(cdesc));
cdesc.dwSize = sizeof(cdesc);
cdesc.dwFlags = DSCBCAPS_CTRLFX;
cdesc.dwBufferBytes = 2000 * NUM_REC_NOTIFICATIONS;//待定
cdesc.lpwfxFormat = &WaveDataFormat;
cdesc.dwFXCount = 2;
cdesc.lpDSCFXDesc = efft;
HWND win = AfxGetApp()->m_pMainWnd->m_hWnd;
hr = DirectSoundFullDuplexCreate8(0, 0,&cdesc, &desc,win,
DSSCL_PRIORITY,&DirectSoundFD, &DirectSoundCaptureBuf8,&DirectSoundBuf8, 0);
DXTRACE_ERR( TEXT("DirectSoundFullDuplexCreate8"), hr );
//成功创徏DirectSoundFDQDirectSoundCaptureBuf8QDirectSoundBuf8Q均不ؓ零?/p>
if(!FAILED(hr))
DirectSoundFD->QueryInterface(IID_IDirectSoundBuffer8, (void**)pIUnkown);
//发现上面的pIUnkown=0Q查询失败,Z么?
AfxMessageBox("p|");
A pointer to the size, in bytes, of the "from" buffer required only if lpFrom is specified.
你会发现,q个参数是一个输入输出?而WSARecvFrom投递的是一个异步的IOCPh,故?Z此方?CUDPRecvSendThread::postRecvRequest)之后,nSenderAddrSizeq个临时变量׃被回?不出事才怪了.
好吧,q事是我干出来?今年竟是做一些脑D的事情.肿么了我q是.T_T
要解册个问?最好的办法是把nSenderAddrSize作ؓCUdpOverLappedRecv的成员变量保?q样生命周期可以得以保证.
好吧,qg脑残事就q么l了.
用FFMPEG SDKq行视频转码压羃的时候,转码成功后去看视频的内容Q发现音视频是不同步的。这个的是一个恼火的事情。我在用FFMPEG SDK做h264格式的FLV文g~码Filter的时候就到了这个问题?/p>
l过研究发现QFFMPEG SDK写入视频的时候有两个地方用来控制写入的时间戳Q一个是AvPacket, 一个是AvFrame?在调用avcodec_encode_video的时候需要传入AvFrame的对象指针,也就是传入一帧未压羃的视频进行压~处理,AvFrame包含一个pts的参敎ͼq个参数是当前帧将来在q原播放的时候的旉戟뀂而AvPacket里面也有ptsQ还有dts。说赯个就必须要说明一下I,P,B三种视频压羃帧。I帧就是关键Q不依赖于其他视频QP帧是向前预测的Q只依赖于前面的视频帧,而B帧是双向预测视频帧,依赖于前后视频。由于B帧的存在Q因为它是双向的Q必ȝ道前面的视频帧和后面的视频的详l内容后Q才能知道本B帧最l该呈现什么图像。而pts和dts两个参数是用来控制视频帧的昄和解码的序?/p>
pts是帧显C的序?/p>
dts是帧被dq行解码的顺序?/p>
如果没有B帧存在,dts和pts是相同的。反之,则是不相同的。关于这个的详细介绍可以参考一下mpeg的原理?/p>
再说说AvPacket中包含的pts和dts两个到底该设|什么|
pts和dts需要设|的是视频帧解码和昄的顺序。每增加一帧就加一Qƈ不是播放视频的时间戳?/p>
但是实践证明l过rmvb解码的视频有时候ƈ不是固定帧率的,而是变率的Q这P如果每压~一帧,pts和dts加一的方案ؓD韌频不同步?/p>
那怎么来解决音视频同步的问题呢Q?/p>
L如下代码Dc?/p>
lTimeStamp 是通过directshow 获取的当前的视频帧的旉戟?/p>
m_llframe_index为当前已l经q压~处理的帧的数量?/p>
首先av_rescale计算得到当前压羃处理已经需要处理什么时间戳的视频Q如果该旉戛_未到达directshow当前提供的视频的时间戳Q则该帧丢弃掉?/p>
否则q行压羃操作。ƈ讄AVPacket的pts和dts。这里假设B帧不存在?/p>
因ؓ在将来播攄时候视频以我们讑֮的固定播攑֓率进行播放,所以需要根据设定的播放帧率计算得到的视频旉戛_directshow提供的当前视频的时间戳q行比较Q设定是否需要进行实施g~播攄{略。如果需要g~播放,则将pts增加步长2Q否则以普通速度播放Q则讄?.dts与之相同?/p>
自绘控g中经怼遇到闪烁的问题,主要原因是某个区域发生了重复性的l制而导致的Q而列表控件在自绘后闪烁原因是WM_ERASEBKGND消息所D的。解军_办法可以这个消息屏蔽掉Q然后用双~存Q在Windows 5.1版本以后可以使用LVS_EX_DOUBLEBUFFER样式Qؓ了各个版本的兼容性我个h不徏议用这个样式,因此我们可以拦截WM_PAINT消息来用我们自已创建的双缓存,具体看代码所C: