zoukankan      html  css  js  c++  java
  • 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 ",input_file_name);

         exit(1);

      }//打开输入文件

      if(av_find_stream_info(ic)<0)

      {

         printf("can't find suitable codecparameters ");

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

             }

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

             {

              //printf("audio ");

                audioindex=i;

             }

      }

       if(videoindex==-1)

       {

              printf("can't find videostream ");

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

         exit(1);

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

      if(avcodec_open(vCodecCtx,vCodec)<0)

      {

         printf("can't open the videodecoder ");

         exit(1);

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

       if(audioindex==-1)

         {

            printf("can't find audiostream ");

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

         exit(1);

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

      if(avcodec_open(aCodecCtx,aCodec)<0)

      {

         printf("can't open the audiodecoder ");

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

               exit(0);

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

        oc=av_alloc_format_context();

        if(!oc)

        {

               printf("Memory error ");

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

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

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

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

               exit(0);

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

        if(avcodec_open(oVcc,oVc)<0)

        {

               printf("can't open the outputvideo codec ");

               exit(0);

        }//打开视频编码器

        oAc=avcodec_find_encoder(CODEC_ID_MP3);

        if(!oAc)

        {

               printf("can't find suitableaudio encoder ");

               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 ",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 ",output_file_name);

                  exit(0);

           }//打开输出文件

        }

        if(!oc->nb_streams)

        {

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

               exit(0);

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

      if(av_write_header(oc)<0)

      {

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

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

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

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

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

                   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

     

  • 相关阅读:
    Vasya and Endless Credits CodeForces
    Dreamoon and Strings CodeForces
    Online Meeting CodeForces
    数塔取数 基础dp
    1001 数组中和等于K的数对 1090 3个数和为0
    1091 线段的重叠
    51nod 最小周长
    走格子 51nod
    1289 大鱼吃小鱼
    POJ 1979 Red and Black
  • 原文地址:https://www.cnblogs.com/sunminmin/p/4512566.html
Copyright © 2011-2022 走看看