打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
ffmpeg入门进修——文档6:同步音频
发布时间:2012-12-15 发布来源:
/** module:div_rill_tb* file name:div_rill_tb.v* syn:no* author:rill* date:2012-09-10*/`timescale 1ns/1nsmodule div_rill_tb;reg clk;reg rst;reg [31:0] a;reg [31:0] b;wire [31:0] yshang;wire [31:0] yyushu;wire calc_done;initialbegin clk = 0; rst = 0; #20 rst = 1; #40 a = ¥random()%10000; b = ¥random()%1000; #1000 a = ¥random()%1000; b = ¥random()%100; #1000 a = ¥random()%100; b = ¥random()%10; #1000 ¥stop;endalways #10 clk = ~clk;div_rill DIV_RILL(.clk (clk),.rst (rst),.a (a),.b (b),.yshang (yshang),.yyushu (yyushu),.calc_done (calc_done));endmodule/******** EOF ******************/
3.3 仿真
友情是心灵的连络。
最长的莫过于时候,因为它永无穷尽;最短的也莫过于时候,因为我们所有的规划都来不及完成。领导6:同步音频
同步音频
如今我们已经有了一个斗劲像样的播放器。所以让我们看一下还有哪些零散的器材没处理惩罚。前次,我们掩盖了一点同步题目,也就是同步音频到视频而不是其它的同 步体式格式。我们将采取和视频一样的体式格式:做一个内部视频时钟来记录视频线程播放了多久,然后同步音频到上方去。后面我们也来看一下如何推而广之把音频和视频 都同步到外部时钟。
生成一个视频时钟
如今我们要生成一个类似于前次我们的声音时钟的视频时钟:一个给出当前视频播放时候的内部值。开端,你可能会想这和应用上一帧的时候戳来更新按时器一样简 单。然则,不要忘了视频帧之间的时候间隔是很长的,以毫秒为计量的。解决办法是跟踪别的一个值:我们在设置上一帧时候戳的时辰的时候值。于是当前视频时候 值就是PTS_of_last_frame + (current_time - time_elapsed_since_PTS_value_was_set)。这种解决体式格式与我们在函数get_audio_clock中的体式格式很类 似。
地点在我们的大布局体中,我们将放上一个双精度浮点变量video_current_pts和一个64位宽整型变量video_current_pts_time。时钟更新将被放在video_refresh_timer函数中。
void video_refresh_timer(void *userdata) {
if(is->video_st) {
if(is->pictq_size == 0) {
schedule_refresh(is, 1);
} else {
vp = &is->pictq[is->pictq_rindex];
is->video_current_pts = vp->pts;
is->video_current_pts_time = av_gettime();
不要忘怀在stream_component_open函数中初始化它:
is->video_current_pts_time = av_gettime();
如今我们须要一种获得信息的体式格式:
double get_video_clock(VideoState *is) {
double delta;
delta = (av_gettime() - is->video_current_pts_time) / 1000000.0;
return is->video_current_pts + delta;}
提取时钟
然则为什么要强迫应用视频时钟呢?我们更改视频同步代码乃至于音频和视频不会试着去彼此同步。想像一下我们让它像ffplay一样有一个号令行参数。所以 让我们抽象一样这件工作:我们将做一个新的封装函数get_master_clock,用来检测av_sync_type变量然后决意调用 get_audio_clock还是get_video_clock或者其它的想应用的获得时钟的函数。我们甚至可以应用电脑时钟,这个函数我们叫做 get_external_clock:
enum {
AV_SYNC_AUDIO_MASTER,
AV_SYNC_VIDEO_MASTER,
AV_SYNC_EXTERNAL_MASTER,};
#define DEFAULT_AV_SYNC_TYPE AV_SYNC_VIDEO_MASTER
double get_master_clock(VideoState *is) {
if(is->av_sync_type == AV_SYNC_VIDEO_MASTER) {
return get_video_clock(is);
} else if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) {
return get_audio_clock(is);
} else {
return get_external_clock(is);
}}
main() {
...
is->av_sync_type = DEFAULT_AV_SYNC_TYPE;...
}
同步音频
如今是最难的项目组:同步音频到视频时钟。我们的策略是测量声音的地位,把它与视频时候斗劲然后算出我们须要批改几许的样本数,也就是说:我们是否须要经由过程丢弃样本的体式格式来加快播放还是须要经由过程插值样本的体式格式来放慢播放?
我们将在每次处理惩罚声音样本的时辰运行一个synchronize_audio的函数来正确的紧缩或者扩大声音样本。然而,我们不想在每次发明有误差的时辰 都进行同步,因为如许会使同步音频多于视频包。所以我们为函数synchronize_audio设置一个最小连气儿值来限制须要同步的时刻,如许我们就不 会老是在调剂了。当然,就像前次那样,“落空同步”意味着声音时钟和视频时钟的差别大于我们的阈值。
所以我们将应用一个分数系数,叫c,所以如今可以说我们获得了N个落空同步的声音样本。落空同步的数量可能会有很多变更,所以我们要策画一下落空同步的长 度的均值。例如,第一次调用的时辰,显示出来我们落空同步的长度为40ms,下次变为50ms等等。然则我们不会应用一个简单的均值,因为间隔如今比来的 值比靠前的值要首要的多。所以我们将应用一个分数体系,叫c,然后用如许的公式来策画差别:diff_sum = new_diff + diff_sum*c。筹办好去找均匀差别的时辰,我们用简单的策画体式格式:avg_diff = diff_sum * (1-c)。
重视:为什么会在这里?这个公式看来很神奇!嗯,它根蒂根基上是一个应用等比级数的加权均匀值。我不知道这是否有名字(我甚至查过维基百科!),然则若是想要更多的信息,这里是一个申明http://www.dranger.com/ffmpeg/weightedmean.html或者在http://www.dranger.com/ffmpeg/weightedmean.txt里。
下面是我们的函数:
int synchronize_audio(VideoState *is, short *samples,
int samples_size, double pts) {
int n;
double ref_clock;
n = 2 * is->audio_st->codec->channels;
if(is->av_sync_type != AV_SYNC_AUDIO_MASTER) {
double diff, avg_diff;
int wanted_size, min_size, max_size, nb_samples;
ref_clock = get_master_clock(is);
diff = get_audio_clock(is) - ref_clock;
if(diff < AV_NOSYNC_THRESHOLD) {
// accumulate the diffs
is->audio_diff_cum = diff &#43; is->audio_diff_avg_coef
* is->audio_diff_cum;
if(is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) {
is->audio_diff_avg_count&#43;&#43;;
} else {
avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef);
}
} else {
is->audio_diff_avg_count = 0;
is->audio_diff_cum = 0;
}
}
return samples_size;}
如今我们已经做得很好;我们已经近&#20284;的知道如何用视频或者其它的时钟来调剂音频了。所以让我们来策画一下要在添加和砍掉几许样本,并且如安在“Shrinking/expanding buffer code”项目组来写上代码:
if(fabs(avg_diff) >= is->audio_diff_threshold) {
wanted_size = samples_size &#43;
((int)(diff * is->audio_st->codec->sample_rate) * n);
min_size = samples_size * ((100 - SAMPLE_CORRECTION_PERCENT_MAX)
/ 100);
max_size = samples_size * ((100 &#43; SAMPLE_CORRECTION_PERCENT_MAX)
/ 100);
if(wanted_size < min_size) {
wanted_size = min_size;
} else if (wanted_size > max_size) {
wanted_size = max_size;
}
记住audio_length * (sample_rate * # of channels * 2)就是audio_length秒时候的声音的样本数。所以,我们想要的样本数就是我们按照声音偏移添加或者削减后的声音样本数。我们也可以设置一个局限来限制我们一次进行批改的长度,因为若是我们改变的太多,用户会听到逆耳的声音。
批改样本数
如今我们要真正的批改一下声音。你可能会重视到我们的同步函数synchronize_audio返回了一个样本数,这可以告诉我们有几许个字节被送到流 中。所以我们只要调剂样本数为wanted_size就可以了。这会让样本更小一些。然则若是我们想让它变大,我们不克不及只是让样本大小变大,因为在缓冲区 中没有多余的数据!所以我们必须添加上去。然则我们如何来添加呢?最笨的办法就是试着来推算声音,所以让我们用已有的数据在缓冲的末尾添加上最后的样本。
if(wanted_size < samples_size) {
samples_size = wanted_size;} else if(wanted_size > samples_size) {
uint8_t *samples_end, *q;
int nb;
nb = (samples_size - wanted_size);
samples_end = (uint8_t *)samples &#43; samples_size - n;
q = samples_end &#43; n;
while(nb > 0) {
memcpy(q, samples_end, n);
q &#43;= n;
nb -= n;
}
samples_size = wanted_size;}
如今我们经由过程这个函数返回的是样本数。我们如今要做的是应用它:
void audio_callback(void *userdata, Uint8 *stream, int len) {
VideoState *is = (VideoState *)userdata;
int len1, audio_size;
double pts;
while(len > 0) {
if(is->audio_buf_index >= is->audio_buf_size) {
audio_size = audio_decode_frame(is, is->audio_buf, sizeof(is->audio_buf), &pts);
if(audio_size < 0) {
is->audio_buf_size = 1024;
memset(is->audio_buf, 0, is->audio_buf_size);
} else {
audio_size = synchronize_audio(is, (int16_t *)is->audio_buf,
audio_size, pts);
is->audio_buf_size = audio_size;
我们要做的是把函数synchronize_audio插入进去。(同时,包管在初始化上方变量的时辰搜检一下代码,这些我没有赘述)。
停止之前的最后一件工作:我们须要添加一个if语句来包管我们不会在视频为主时钟的时辰也来同步视频。
if(is->av_sync_type != AV_SYNC_VIDEO_MASTER) {
ref_clock = get_master_clock(is);
diff = vp->pts - ref_clock;
sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay :
AV_SYNC_THRESHOLD;
if(fabs(diff) < AV_NOSYNC_THRESHOLD) {
if(diff <= -sync_threshold) {
delay = 0;
} else if(diff >= sync_threshold) {
delay = 2 * delay;
}
}}
添加后就可以了。要包管全部法度中我没有赘述的变量都被初始化过了。然后编译它:
gcc -o tutorial06 tutorial06.c -lavutil -lavformat -lavcodec -lz -lm`sdl-config --cflags --libs`
然后你就可以运行它了。
下次我们要做的是让你可以让电影快退和快进。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
ffplay的音视频同步分析
Gstreamer的一些基本概念与A/V同步分析
十、详解FFplay音视频同步
顺芯推出ES8156高性能立体声音频DAC达到110DB 附设计电路
Gstreamer的音视频同步
为什么“我们有95%的信心”这种说法不完全对?
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服