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

     

  • 相关阅读:
    MFC单文档中进行plc点云显示
    modbus发送和接收
    测试左移和测试右移
    2021软件测试笔试题
    202106月份总结测试开发面试题
    KuaiShou面试题
    阿拉伯数学手写体。
    GeminiScrollbar
    vue-core-video-player
    webpack
  • 原文地址:https://www.cnblogs.com/sunminmin/p/4512566.html
Copyright © 2011-2022 走看看