FFmpeg--音视频同步

output_example.c 中AV同步的代码如下(我的代码有些修改),这个实现相当简单,不过挺说明问题。

音视频同步方法:选择一个参考时钟,参考时钟上的时间是线性递增的,生成数据流时依据参考时钟上的时间给每个数据块

都打上时间戳(一般包括开始时间和结束时间)。在播放时,读取数据块上的时间戳,同时参考当前参考时钟上的时间来安

排播放。数据流不会发生参考关系。

步骤:
1, 首先分离器分解为音频和视频数据流

2,输出以前进行时间戳比较,相同则是同步的,直接输出。

3,不同的则经过同步函数进行调整之后再输出

decoder 可以根据frame rate 计算每个frame 的间隔时间,只要知道第一个frame 的pts,后面的pts就可以根据frame rate计算出来。

PTS:presentation time stamp 显示时间戳
DTS主要用于视频的解码,在解码阶段使用.PTS主要用于视频的同步和输出.在display的时候使用.在没有B frame的情况

下.DTS和PTS的输出顺序是一样的.

阅读前希望大家先了解一下时间戳的概念。

/*compute current audio and video time */

if(pOutputVars->pOutAudio_st)//存在音频流

pOutputVars->audio_pts =(double)pOutputVars->pOutAudio_st->pts.val *pOutputVars->pOutAudio_st->time_base.num
/ pOutputVars->pOutAudio_st->time_base.den; //(pts是时间戳结构)输出音频的时间戳,
转换为基准时间

else

pOutputVars->audio_pts = 0.0;

if(pOutputVars->pOutVideo_st)

pOutputVars->video_pts =(double)pOutputVars->pOutVideo_st->pts.val *pOutputVars->pOutVideo_st->time_base.num
/ pOutputVars->pOutVideo_st->time_base.den;//输出视频时间戳

else

pOutputVars->video_pts = 0.0;

if(!pOutputVars->pOutAudio_st && !pOutputVars->pOutVideo_st)

return 0;

/*write interleaved audio and video frames */

if(!pOutputVars->pOutVideo_st
|| (pOutputVars->pOutVideo_st &&pOutputVars->pOutAudio_st
&& pOutputVars->audio_pts
<

pOutputVars->video_pts)) {

write_audio_frame(pOutputVars->pOutFormatCtx,pOutputVars->pOutAudio_st, pInputAudioBuf);//没有视频流,或者音频流时间没赶上视频流

(通过比较时间戳),
则输出(编码输出)音频祯数据

} else {

write_video_frame(pOutputVars->pOutFormatCtx,pOutputVars->pOutVideo_st, pInputVedioFrame);//否则输出视频祯数据

}

输出数据的时间戳怎么得到的,以音频为例:

pkt.size= avcodec_encode_audio(c,audio_outbuf, audio_outbuf_size, pInputAudioBuf);//源数据应该包含时间戳,pInputAudioBuf是源文件解码后的音频数据

pkt.pts=av_rescale_q(c->coded_frame->pts, c->time_base, st->time_base);//编码后的祯也含有源文件的时间戳,这个函数应该是转换同时间基准,没研究过

pkt.flags |= PKT_FLAG_KEY;

pkt.stream_index= st->index;

pkt.data= audio_outbuf;

...

应该就是这么个过程了,然后用av_write_frame(oc,&pkt),
把音频祯和视频祯交错写入到输出文件.
通过上面分析,可以看到,有时候可能连续写几个音频

祯或视频祯.

播放时的同步可能ffplay中有,还没细看

实现转码一个普通视频文件为视频mpeg4,音频mp3的功能的程序

本程序实现转码一个普通视频文件为视频mpeg4,音频mp3的功能

#include<avcodec.h>

#include<avformat.h>

#include<stdio.h>

#include<avutil.h>

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

main(intargc,char **argv)

{

const char*input_file_name="/root/movies/ddh1.mpg";

av_register_all();//注册库中所有可用的文件格式和编码器

AVFormatContext *ic;

//输入文件处理部分

ic=av_alloc_format_context();

if(av_open_input_file(&ic,input_file_name,NULL,0,NULL)!=0)

{

printf("can‘t open the file%s\n",input_file_name);

exit(1);

}//打开输入文件

if(av_find_stream_info(ic)<0)

{

printf("can‘t find suitable codecparameters\n");

exit(1);

}//取出流信息

dump_format(ic,0,input_file_name,0);//列出输入文件的相关流信息

int i;

int videoindex=-1;int audioindex=-1;

for(i=0;i<ic->nb_streams;i++)

{

if(ic->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO)

{

videoindex=i;

//printf("video\n");

}

elseif(ic->streams[i]->codec->codec_type==CODEC_TYPE_AUDIO)

{

//printf("audio\n");

audioindex=i;

}

}

if(videoindex==-1)

{

printf("can‘t find videostream\n");

exit(1);

}//没有找到视频流

AVCodecContext *vCodecCtx;

vCodecCtx=ic->streams[videoindex]->codec;//取得视频流编码上下文指针

AVCodec *vCodec;

vCodec=avcodec_find_decoder(vCodecCtx->codec_id); 
找到coder

if(vCodec==NULL)

{

printf("can‘t find suitable videodecoder\n");

exit(1);

}//找到合适的视频解码器

if(avcodec_open(vCodecCtx,vCodec)<0)

{

printf("can‘t open the videodecoder\n");

exit(1);

}//打开该视频解码器 
总之先找到coder
再打开它

if(audioindex==-1)

{

printf("can‘t find audiostream\n");

exit(1);

}//没有找到音频流

AVCodecContext *aCodecCtx;

aCodecCtx=ic->streams[audioindex]->codec;

AVCodec *aCodec;

aCodec=avcodec_find_decoder(aCodecCtx->codec_id);

if(aCodec==NULL)

{

printf("can‘t find suitable audiodecoder\n");

exit(1);

}//找到合适的音频解码器

if(avcodec_open(aCodecCtx,aCodec)<0)

{

printf("can‘t open the audiodecoder\n");

exit(1);

}//打开该音频解码器

//下面为输出文件处理部分

const char*output_file_name="/root/123.avi";

AVOutputFormat *fmt;

AVFormatContext *oc;

AVCodecContext *oVcc,*oAcc;

AVCodec *oVc,*oAc;

AVStream *video_st,*audio_st;

AVFrame *oVFrame,*oAFrame;

double video_pts;

oVFrame=avcodec_alloc_frame();

fmt=guess_format(NULL,output_file_name,NULL);

if(!fmt)

{

printf("could not deduce outputformat from outfile extension\n");

exit(0);

}//判断是否可以判断输出文件的编码格式

oc=av_alloc_format_context();

if(!oc)

{

printf("Memory error\n");

exit(0);

}

oc->oformat=fmt;

pstrcpy(oc->filename,sizeof(oc->filename),output_file_name);

video_st=av_new_stream(oc,0);

if(!video_st)

{

printf("could not alloc videostream\n");

exit(0);

}

oVcc=avcodec_alloc_context();

oVcc=video_st->codec;

oVcc->codec_id=CODEC_ID_MPEG4;

oVcc->codec_type=CODEC_TYPE_VIDEO;

oVcc->bit_rate=2500000;

oVcc->width=704;

oVcc->height=480;

oVcc->time_base=vCodecCtx->time_base;

oVcc->gop_size=vCodecCtx->gop_size;

//oVcc->pix_fmt=vCodecCtx->pix_fmt;

oVcc->pix_fmt=vCodecCtx->pix_fmt;

oVcc->max_b_frames=vCodecCtx->max_b_frames;

video_st->r_frame_rate=ic->streams[videoindex]->r_frame_rate;

audio_st=av_new_stream(oc,oc->nb_streams);

if(!audio_st)

{

printf("could not alloc audiostream\n");

exit(0);

}

avcodec_get_context_defaults2(audio_st->codec,CODEC_TYPE_AUDIO);

oAcc=avcodec_alloc_context();

oAcc=audio_st->codec;

oAcc->codec_id=CODEC_ID_MP3;

oAcc->codec_type=CODEC_TYPE_AUDIO;

oAcc->bit_rate=aCodecCtx->bit_rate;

oAcc->sample_rate=aCodecCtx->sample_rate;

oAcc->channels=2;

if (av_set_parameters(oc, NULL) < 0)

{

printf( "Invalid output formatparameters\n");

exit(0);

}//设置必要的输出参数

strcpy(oc->title,ic->title);

strcpy(oc->author,ic->author);

strcpy(oc->copyright,ic->copyright);

strcpy(oc->comment,ic->comment);

strcpy(oc->album,ic->album);

oc->year=ic->year;

oc->track=ic->track;

strcpy(oc->genre,ic->genre);

dump_format(oc,0,output_file_name,1);//列出输出文件的相关流信息

oVc=avcodec_find_encoder(CODEC_ID_MPEG4);

if(!oVc)

{

printf("can‘t find suitable videoencoder\n");

exit(0);

}//找到合适的视频编码器

if(avcodec_open(oVcc,oVc)<0)

{

printf("can‘t open the outputvideo codec\n");

exit(0);

}//打开视频编码器

oAc=avcodec_find_encoder(CODEC_ID_MP3);

if(!oAc)

{

printf("can‘t find suitableaudio encoder\n");

exit(0);

}//找到合适的音频编码器

if(avcodec_open(oAcc,oAc)<0)

{

printf("can‘t open the outputaudio codec");

exit(0);

}//打开音频编码器

/*if(url_exist(output_file_name))

{

printf("the output file name %s hasexist,please select other\n",output_file_name);

exit(0);

}//判断该输出文件是否已经存在*/

if (!(oc->flags & AVFMT_NOFILE))

{

if(url_fopen(&oc->pb,output_file_name,URL_WRONLY)<0)

{

printf("can‘t open theoutput file %s\n",output_file_name);

exit(0);

}//打开输出文件

}

if(!oc->nb_streams)

{

fprintf(stderr,"output filedose not contain any stream\n");

exit(0);

}//查看输出文件是否含有流信息

if(av_write_header(oc)<0)

{

fprintf(stderr, "Could not writeheader for output file\n");

exit(1);

}[/i][/i]

AVPacketpacket;

uint8_t *ptr,*out_buf;

int out_size;

static short *samples=NULL;

static unsigned int samples_size=0;

uint8_t *video_outbuf,*audio_outbuf;int video_outbuf_size,audio_outbuf_size;

video_outbuf_size=400000;

video_outbuf= (unsigned char *)malloc(video_outbuf_size);

audio_outbuf_size = 10000;

audio_outbuf = av_malloc(audio_outbuf_size);

int flag;int frameFinished;int len;intframe_index=0,ret;

while(av_read_frame(ic,&packet)>=0)//从输入文件中读取一个包

{

if(packet.stream_index==videoindex)//判断是否为当前视频流中的包

{

len=avcodec_decode_video(vCodecCtx,oVFrame,&frameFinished,packet.data,packet.size);//若为视频包,解码该视频包

if(len<0)

{

printf("Error whiledecoding\n");

exit(0);

}

if(frameFinished)//判断视频祯是否读完

{

fflush(stdout);

oVFrame->pts=av_rescale(frame_index,AV_TIME_BASE*(int64_t)oVcc->time_base.num,oVcc->time_base.den);

oVFrame->pict_type=0;

out_size =avcodec_encode_video(oVcc, video_outbuf, video_outbuf_size, oVFrame);

if (out_size > 0)

{

AVPacket pkt;

av_init_packet(&pkt);

if(oVcc->coded_frame&& oVcc->coded_frame->key_frame)

pkt.flags |=PKT_FLAG_KEY;

pkt.flags =packet.flags;

pkt.stream_index=video_st->index;

pkt.data=video_outbuf;

pkt.size= out_size;

ret=av_write_frame(oc,&pkt);

}

frame_index++;

}

else

ret=av_write_frame(oc,&packet);

//img_convert((AVPicture*)vFrame, PIX_FMT_RGB24, (AVPicture*)oVFrame, oVcc->pix_fmt,oVcc->width,oVcc-

>height);

//SaveFrame(vFrame,oVcc->width,oVcc->height,frame_index);

if(ret!=0)

{

printf("while writevideo frame error\n");

exit(0);

}

}

elseif(packet.stream_index==audioindex)

{

len=packet.size;

ptr=packet.data;

int ret=0;

while(len>0)

{

out_buf=NULL;

out_size=0;

if(&packet)

samples=av_fast_realloc(samples,&samples_size,FFMAX(packet.size*sizeof

(*samples),AVCODEC_MAX_AUDIO_FRAME_SIZE));

out_size=samples_size;

ret=avcodec_decode_audio(aCodecCtx,samples,&out_size,ptr,len);//若为音频包,解码该音频包

if(ret<0)

{

printf("whiledecode audio failure\n");

exit(0);

}

fflush(stdout);

ptr+=ret;

len-=ret;

if(out_size<=0)

continue;

out_buf=(uint8_t *)samples;

AVPacket pkt;

av_init_packet(&pkt);

pkt.size=avcodec_encode_audio(oAcc, audio_outbuf, audio_outbuf_size, out_buf);

pkt.pts=av_rescale_q(oAcc->coded_frame->pts, oAcc->time_base,audio_st->time_base);

pkt.flags |= PKT_FLAG_KEY;

pkt.stream_index= audioindex;

pkt.data= audio_outbuf;

if (av_write_frame(oc, &pkt) !=0)

{

fprintf(stderr, "Errorwhile writing audio frame\n");

exit(1);

}

}

}

av_free_packet(&packet);

}

av_write_trailer(oc);

for(i= 0; i < oc->nb_streams; i++)

{

av_freep(&oc->streams[i]->codec);

av_freep(&oc->streams[i]);

}

url_fclose(oc);

av_free(oc);

av_free(oVFrame);

av_free(out_buf);

avcodec_close(vCodecCtx);

avcodec_close(aCodecCtx);

av_close_input_file(ic);

}

来源:http://blog.csdn.net/yangzhiloveyou/article/details/8832516

时间: 2024-07-30 01:00:50

FFmpeg--音视频同步的相关文章

ffmpeg转码MPEG2-TS的音视频同步机制分析

http://blog.chinaunix.net/uid-26000296-id-3483782.html 一.FFmpeg忽略了adaptation_field()数据FFmpeg忽略了包含PCR值的adaptation_filed数据; 代码(libavformat/mpegts.c)分析如下: /* 解析TS包 */int handle_packet(MpegTSContext *ts, const uint8_t *packet){  ...   pid = AV_RB16(packe

音视频同步问题

音视频同步问题 音视频流里都包含了播放速率的信息,音频使用采样率来表示,而视频则采用f/s来表示,但是我们却不能简单地用这两个数据来对音视频进行同步,我们需要使用DTS(解码时间戳)和PTS(播放时间戳)这两个数据:我们知道影视数据在存储时,会存在多种帧形式,例如MPEG中就采用了I,B和P,由于B帧的存在使得PTS和DTS存在不同(原因见附录),如图1所示为一个简单的例子:当然真正影响我们音视频同步的是PTS. 我们可以从影视文件中获得包的PTS,但是我们无法直接获得帧(我们真正关心的)的PT

ffplay(2.0.1)中的音视频同步

最近在看ffmpeg相关的一些东西,以及一些播放器相关资料和代码. 然后对于ffmpeg-2.0.1版本下的ffplay进行了大概的代码阅读,其中这里把里面的音视频同步,按个人的理解,暂时在这里作个笔记. 在ffplay2.0.1版本里面,视频的刷新不再直接使用SDL里面的定时器了,而是在主的循环中event_loop中,通过调用函数refresh_loop_wait_event来等待事件, 同时在这个refresh_loop_wait_event函数里面,通过使用休眠函数av_usleep 来

vlc源码分析(五) 流媒体的音视频同步

http://www.cnblogs.com/jiayayao/p/6890882.html vlc播放流媒体时实现音视频同步,简单来说就是发送方发送的RTP包带有时间戳,接收方根据此时间戳不断校正本地时钟,播放音视频时根据本地时钟进行同步播放.首先了解两个概念:stream clock和system clock.stream clock是流时钟,可以理解为RTP包中的时间戳:system clock是本地时钟,可以理解为当前系统的Tick数.第一个RTP包到来时: fSyncTimestamp

通俗的解释下音视频同步里pcr作用

PCR同步在非硬件精确时钟源的情况还是谨慎使用,gstreamer里面采用PCR同步,但是发现好多ffmpeg转的片儿,或者是CP方的片源,pcr打得很粗糙的,老是有跳帧等现象.音视频同步,有三种方法,视频同步音频,音频同步视频,同步到外部时钟! 第三种,同步到外部时钟也就是PCR同步,和我上面说的那种同步方式,一样! 用的最多的还是,视频同步音频,为什么呢?音频的采样率是固定的,若音频稍有卡顿,都会很明显的听出来,反则视频则不如此,虽然表面上说的是30fps,不一定每一帧的间隔就必须精确到33

FFmpeg音视频编解码实践总结

PS:由于目前开发RTSP服务器 传输模块时用到了h264文件,所以攻了一段时间去实现h264的视频编解码,借用FFmpeg SDK实现了任意文件格式之间的转换,并实现了流媒体实时播放,目前音视频同步需要稍加完善,视频编码代码已成功移植到Visual Stdio平台,如有需要的留下邮箱 以下文档来自FFmpeg工程组(http://www.ffmpeg.com.cn/index.php开发事例) 实现转码一个普通视频文件为视频mpeg4,音频mp3的功能的程序 本程序源引自FFmpeg工程组,实

音视频同步通讯SDK

视频流中的DTS/PTS到底是什么? DTS(解码时间戳)和PTS(显示时间戳)分别是解码器进行解码和显示帧时相对于SCR(系统参考)的时间戳.SCR可以理解为解码器应该开始从磁盘读取数据时的时间. mpeg文件中的每一个包都有一个SCR时间戳并且这个时间戳就是读取这个数据包时的系统时间.通常情况下,解码器会在它开始读取mpeg流时启动系统时钟(系统时钟的初始值是第一个数据包的SCR值,通常为0但也可以不从0开始). DTS 时间戳决定了解码器在SCR时间等于DTS时间时进行解码,PTS时间戳也

FFmpeg音视频核心技术精讲与实战

第1章 课程导学与准备工作全民娱乐时代,需要音视频等多媒体产品层出不穷,但会处理音视频数据的工程师却极度匮乏,进入音视频开发领域正当时,这门课程就是为这样的你而生.来吧!加入我们,成就更好的自己.1-1 课前必读(不看会错过一个亿)1-2 课程导学1-3 音视频的应用范围与播放器架构讲解(选看)1-4 什么是ffmpeg?它能做什么?(选看)1-5 ffmpeg下载,编译与安装1-6 Windows下安装 FFmpeg1-7 ffmpeg命令大全文档 第2章 FFmpeg常用命令实战本章讲解如何

(转)音视频同步-时间戳

媒体内容在播放时,最令人头痛的就是音视频不同步.从技术上来说,解决音视频同步问题的最佳方案就是时间戳:首先选择一个参考时钟(要求参考时钟上的时间是线性递增的):生成数据流时依据参考时钟上的时间给每个数据块都打上时间戳(一般包括开始时间和结束时间):在播放时,读取数据块上的时间戳,同时参考当前参考时钟上的时间来安排播放(如果数据块的开始时间大于当前参考时钟上的时间,则不急于播放该数据块,直到参考时钟达到数据块的开始时间:如果数据块的开始时间小于当前参考时钟上的时间,则“尽快”播放这块数据或者索性将

libstagefright 音视频同步方案

1:音视频数据都有一个list,用于存放解码后的数据: List mFilledBuffers; 2:解码后的音视频数据不断的往list中存放,不做音视频同步方面的时间上控制 mFilledBuffers.push_back(i); 3:音视频同步主要表现在从list中取数据进行视频的显示和音频的输出: 4:其中audio数据在线程函数threadLoop中调用AudioPlayer的回调函数循环读取,不做时间上的控制: 4:视频数据正常情况下按照每10ms的时间取一次,如果有音视频不同步的情况