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);
}