解决ffmpeg中的时间戳同步问题_day95

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解决ffmpeg中的时间戳同步问题_day95相关的知识,希望对你有一定的参考价值。

参考技术A PTS/DTS(时间戳
要想解决时间同步问题就必须要了解ffmpeg中的PTS和DTS到底是什么

PTS:

PTS(Presentation TimeStamp)是渲染用的时间戳,播放器会根据这个时间戳进行渲染播放

DTS:

DTS(Decoding TimeStamp)解码时间戳,在视频packet进行解码成frame的时候会使用到

有了PTS为什么还需要DTS

就拿编码H264来说,H264编码分为I帧,B帧,P帧,I帧是关键帧,也就是一个GOP的最开始帧,B帧是前后参考帧,它属于帧间压缩技术,B帧会通过记录前后两帧进行压缩,P帧是向前参考帧,比B帧的压缩率要低

因为B帧要参考前后帧,那么在编码之后的帧的时间顺序就会发生变化,所以在没有B帧的时候PTS和DTS应该是一样的,有了B帧之后PTS和DTS也就会发生变化

时间基
有了时间戳之后,还需要将PTS的时间戳转成以秒为单位的时间。这里就需要用到ffmpeg的时间基来进行计算了

先了解一下tbr,tbn,tbc

tbr: 是通常说的帧率
tbn: 视频流的时间基
tbc: 时间解码的时间基
在ffmpeg中,不同的时间戳对应不同的时间基。一般在视频进行渲染的时候渲染的时候用到的就是视频流的时间基tbn,视频流的时间基和帧率有关,每秒30帧,代表一帧需要1/30秒

ffmpeg内部有自己的时间基,分别定义了

// ffmpeg中内部的时间基

// ffmpeg中分数所表示法

av_q2d,计算真实秒数

// ffmpeg 中进行转换,将时间转成秒,av_q2d可以将时间基转换成double类型小数
typedef struct AVRational
int num; //numerator
int den; //denominator
AVRational;
static inline double av_q2d(AVRational a){
/**

av_rescale_q(),不同的时间基直接的转换

/**

/*
@param a : 需要进行转换的时间戳
@bq : 对应转换的时间戳的原来的时间基
@cq : 转换之后的时间基

*/
总结
通过ffmpeg的时间戳和时间基的使用,可以有效的解决音视频的同步的问题

ffmpeg 如何音视频同步

转自:http://blog.csdn.net/yangzhiloveyou/article/details/8832516

 

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

}

以上是关于解决ffmpeg中的时间戳同步问题_day95的主要内容,如果未能解决你的问题,请参考以下文章

FFMPEG Webvtt m3u8下载成单个vtt文件,忽略“X-TIMESTAMP-MAP=MPEGTS=”(时间戳同步问题)

ffmpeg推流中断与画质不清晰的解决办法

FFmpeg concat 视频和音频不同步

ffmpeg 如何音视频同步

day18-多线程&线程同步&死锁

day18-多线程&线程同步&死锁