ChinaFFmpeg

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 13241|回复: 5

ffmpeg中时间戳同步问题

[复制链接]
发表于 2013-9-30 13:59:25 | 显示全部楼层 |阅读模式
ffmpeg中的正确的pts应该是怎样变化?怎么实现音频同步到视频。。。
回复

使用道具 举报

发表于 2013-9-30 14:03:25 | 显示全部楼层
  1.     if (oc->oformat->flags & AVFMT_RAWPICTURE) {
  2.         /* Raw video case - directly store the picture in the packet */
  3.         AVPacket pkt;
  4.         av_init_packet(&pkt);

  5.         pkt.flags        |= AV_PKT_FLAG_KEY;
  6.         pkt.stream_index  = st->index;
  7.         pkt.data          = dst_picture.data[0];
  8.         pkt.size          = sizeof(AVPicture);

  9.         ret = av_interleaved_write_frame(oc, &pkt);
  10.     } else {
  11.         AVPacket pkt = { 0 };
  12.         int got_packet;
  13.         av_init_packet(&pkt);

  14.         /* encode the image */
  15.         ret = avcodec_encode_video2(c, &pkt, frame, &got_packet);  ////看这里
  16.         if (ret < 0) {
  17.             fprintf(stderr, "Error encoding video frame: %s\n", av_err2str(ret));
  18.             exit(1);
  19.         }
  20.         /* If size is zero, it means the image was buffered. */

  21.         if (!ret && got_packet && pkt.size) {
  22.             pkt.stream_index = st->index;

  23.             /* Write the compressed frame to the media file. */
  24.             ret = av_interleaved_write_frame(oc, &pkt);
  25.         } else {
  26.             ret = 0;
  27.         }
  28.     }
复制代码
根据上面的代码可以知道,是在encode 的时候计算出来的pkt的pts的,当然,你也可以在encode以后去修改pts

那么encode怎么得到的pkt呢
可以看encode里面的具体实现
  1. int attribute_align_arg avcodec_encode_video2(AVCodecContext *avctx,
  2.                                               AVPacket *avpkt,
  3.                                               const AVFrame *frame,
  4.                                               int *got_packet_ptr)
  5. {
  6.     int ret;
  7.     AVPacket user_pkt = *avpkt;
  8.     int needs_realloc = !user_pkt.data;

  9.     *got_packet_ptr = 0;

  10.     if(CONFIG_FRAME_THREAD_ENCODER &&
  11.        avctx->internal->frame_thread_encoder && (avctx->active_thread_type&FF_THREAD_FRAME))
  12.         return ff_thread_video_encode_frame(avctx, avpkt, frame, got_packet_ptr);

  13.     if ((avctx->flags&CODEC_FLAG_PASS1) && avctx->stats_out)
  14.         avctx->stats_out[0] = '\0';

  15.     if (!(avctx->codec->capabilities & CODEC_CAP_DELAY) && !frame) {
  16.         av_free_packet(avpkt);
  17.         av_init_packet(avpkt);
  18.         avpkt->size = 0;
  19.         return 0;
  20.     }   

  21.     if (av_image_check_size(avctx->width, avctx->height, 0, avctx))
  22.         return AVERROR(EINVAL);

  23.     av_assert0(avctx->codec->encode2);

  24.     ret = avctx->codec->encode2(avctx, avpkt, frame, got_packet_ptr); ///////看这里
  25.     av_assert0(ret <= 0);

  26.     if (avpkt->data && avpkt->data == avctx->internal->byte_buffer) {
  27.         needs_realloc = 0;
  28.         if (user_pkt.data) {
  29.             if (user_pkt.size >= avpkt->size) {
  30.                 memcpy(user_pkt.data, avpkt->data, avpkt->size);
  31.             } else {
  32.                 av_log(avctx, AV_LOG_ERROR, "Provided packet is too small, needs to be %d\n", avpkt->size);
  33.                 avpkt->size = user_pkt.size;
  34.                 ret = -1;
  35.             }   
  36.             avpkt->buf      = user_pkt.buf;
复制代码
这个encode代码是在libavcodec/utils.c里面

在ffmpeg里面这个utils是一个主要的switcher,每一个codec都是以模块的形式加载的,通过这个switcher进入对应的编解码模块中
下面可以假设是mpeg4编码,那么就进入如下接口中

  1. AVCodec ff_mpeg4_encoder = {
  2.     .name           = "mpeg4",
  3.     .type           = AVMEDIA_TYPE_VIDEO,
  4.     .id             = AV_CODEC_ID_MPEG4,
  5.     .priv_data_size = sizeof(MpegEncContext),
  6.     .init           = encode_init,
  7.     .encode2        = ff_MPV_encode_picture,  //////进入到这个接口中,这个在registerall的时候就已经注册了
  8.     .close          = ff_MPV_encode_end,
  9.     .pix_fmts       = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE },
  10.     .capabilities   = CODEC_CAP_DELAY | CODEC_CAP_SLICE_THREADS,
  11.     .long_name      = NULL_IF_CONFIG_SMALL("MPEG-4 part 2"),
  12.     .priv_class     = &mpeg4enc_class,
  13. };
复制代码
如果使用的mpeg4编码时,前面的可以跟进入这个 ff_MPV_encode_picture中

  1.        pkt->pts = s->current_picture.f.pts;
  2.         if (!s->low_delay && s->pict_type != AV_PICTURE_TYPE_B) {
  3.             if (!s->current_picture.f.coded_picture_number)
  4.                 pkt->dts = pkt->pts - s->dts_delta;
  5.             else
  6.                 pkt->dts = s->reordered_pts;
  7.             s->reordered_pts = pkt->pts;
  8.         } else
  9.             pkt->dts = pkt->pts;
  10.         if (s->current_picture.f.key_frame)
  11.             pkt->flags |= AV_PKT_FLAG_KEY;
  12.         if (s->mb_info)
  13.             av_packet_shrink_side_data(pkt, AV_PKT_DATA_H263_MB_INFO, s->mb_info_size);
复制代码
从这段代码可以看出,pts是从s->current_picture.f.pts中获得的,然后根据判断图像是否有延迟,然后进行pts-dts_delta计算得来的
这个 s->current_picture.f.pts即为AVFrame的pts,至于这个AVFrame的pts怎么得到的,可以找到如下实现,也是mpeg4里面计算得到的

  1. static int load_input_picture(MpegEncContext *s, const AVFrame *pic_arg)
  2. {
  3.     Picture *pic = NULL;
  4.     int64_t pts;
  5.     int i, display_picture_number = 0, ret;
  6.     const int encoding_delay = s->max_b_frames ? s->max_b_frames :
  7.                                                  (s->low_delay ? 0 : 1);
  8.     int direct = 1;

  9.     if (pic_arg) {
  10.         pts = pic_arg->pts;
  11.         display_picture_number = s->input_picture_number++;

  12.         if (pts != AV_NOPTS_VALUE) {
  13.             if (s->user_specified_pts != AV_NOPTS_VALUE) {
  14.                 int64_t last = s->user_specified_pts;

  15.                 if (pts <= last) {
  16.                     av_log(s->avctx, AV_LOG_ERROR,
  17.                            "Invalid pts (%"PRId64") <= last (%"PRId64")\n",
  18.                            pts, last);
  19.                     return AVERROR(EINVAL);
  20.                 }

  21.                 if (!s->low_delay && display_picture_number == 1)
  22.                     s->dts_delta = pts - last;
  23.             }
  24.             s->user_specified_pts = pts;
  25.         } else {
  26.             if (s->user_specified_pts != AV_NOPTS_VALUE) {
  27.                 s->user_specified_pts =
  28.                 pts = s->user_specified_pts + 1;
  29.                 av_log(s->avctx, AV_LOG_INFO,
  30.                        "Warning: AVFrame.pts=? trying to guess (%"PRId64")\n",
  31.                        pts);
  32.             } else {
  33.                 pts = display_picture_number;
  34.             }
  35.         }
  36.     }
  37.    if (pic_arg) {
  38.         if (!pic_arg->buf[0])
  39.             direct = 0;
  40.         if (pic_arg->linesize[0] != s->linesize)
  41.             direct = 0;
  42.         if (pic_arg->linesize[1] != s->uvlinesize)
  43.             direct = 0;
  44.         if (pic_arg->linesize[2] != s->uvlinesize)
  45.             direct = 0;

  46.         av_dlog(s->avctx, "%d %d %d %d\n", pic_arg->linesize[0],
  47.                 pic_arg->linesize[1], s->linesize, s->uvlinesize);

  48.         if (direct) {
  49.             i = ff_find_unused_picture(s, 1);
  50.             if (i < 0)
  51.                 return i;

  52.             pic = &s->picture[i];
  53.             pic->reference = 3;

  54.             if ((ret = av_frame_ref(&pic->f, pic_arg)) < 0)
  55.                 return ret;
  56.             if (ff_alloc_picture(s, pic, 1) < 0) {
  57.                 return -1;
  58.             }
  59.         } else {
  60.             i = ff_find_unused_picture(s, 0);
  61.             if (i < 0)
  62.                 return i;

  63.             pic = &s->picture[i];
  64.             pic->reference = 3;

  65.             if (ff_alloc_picture(s, pic, 0) < 0) {
  66.                 return -1;
  67.             }

  68.             if (pic->f.data[0] + INPLACE_OFFSET == pic_arg->data[0] &&
  69.                 pic->f.data[1] + INPLACE_OFFSET == pic_arg->data[1] &&
  70.                 pic->f.data[2] + INPLACE_OFFSET == pic_arg->data[2]) {
  71.                 // empty
  72.             } else {
  73.                int h_chroma_shift, v_chroma_shift;
  74.                 av_pix_fmt_get_chroma_sub_sample(s->avctx->pix_fmt,
  75.                                                  &h_chroma_shift,
  76.                                                  &v_chroma_shift);

  77.                 for (i = 0; i < 3; i++) {
  78.                     int src_stride = pic_arg->linesize[i];
  79.                     int dst_stride = i ? s->uvlinesize : s->linesize;
  80.                     int h_shift = i ? h_chroma_shift : 0;
  81.                     int v_shift = i ? v_chroma_shift : 0;
  82.                     int w = s->width  >> h_shift;
  83.                     int h = s->height >> v_shift;
  84.                     uint8_t *src = pic_arg->data[i];
  85.                     uint8_t *dst = pic->f.data[i];

  86.                     if (s->codec_id == AV_CODEC_ID_AMV && !(s->avctx->flags & CODEC_FLAG_EMU_EDGE)) {
  87.                         h = ((s->height + 15)/16*16) >> v_shift;
  88.                     }

  89.                     if (!s->avctx->rc_buffer_size)
  90.                         dst += INPLACE_OFFSET;

  91.                     if (src_stride == dst_stride)
  92.                         memcpy(dst, src, src_stride * h);
  93.                     else {
  94.                         int h2 = h;
  95.                         uint8_t *dst2 = dst;
  96.                         while (h2--) {
  97.                             memcpy(dst2, src, w);
  98.                             dst2 += dst_stride;
  99.                             src += src_stride;
  100.                         }
  101.                     }
  102.                     if ((s->width & 15) || (s->height & 15)) {
  103.                         s->dsp.draw_edges(dst, dst_stride,
  104.                                           w, h,
  105.                                           16>>h_shift,
  106.                                           16>>v_shift,
  107.                                           EDGE_BOTTOM);
  108.                     }
  109.                 }
  110.             }
  111.         }
  112.         ret = av_frame_copy_props(&pic->f, pic_arg);
  113.         if (ret < 0)
  114.             return ret;

  115.         pic->f.display_picture_number = display_picture_number;
  116.         pic->f.pts = pts; // we set this here to avoid modifiying pic_arg
  117.     }

  118.     /* shift buffer entries */
  119.     for (i = 1; i < MAX_PICTURE_COUNT /*s->encoding_delay + 1*/; i++)
  120.         s->input_picture[i - 1] = s->input_picture[i];

  121.     s->input_picture[encoding_delay] = (Picture*) pic;

  122.     return 0;
  123. }

复制代码
从这段代码中可以看到AVFrame的pts或得到了


至于你说的音视频同步,是通过一个flag
video_sync_method或者audio_sync_method

  1.     format_video_sync = video_sync_method;
  2.     if (format_video_sync == VSYNC_AUTO)
  3.         format_video_sync = (s->oformat->flags & AVFMT_VARIABLE_FPS) ? ((s->oformat->flags & AVFMT_NOTIMESTAMPS) ? VSYNC_PASSTHROUGH : VSYNC_VFR) : VSYNC_CFR;

  4.     switch (format_video_sync) {
  5.     case VSYNC_CFR:
  6.         // FIXME set to 0.5 after we fix some dts/pts bugs like in avidec.c
  7.         if (delta < -1.1)
  8.             nb_frames = 0;
  9.         else if (delta > 1.1)
  10.             nb_frames = lrintf(delta);
  11.         break;
  12.     case VSYNC_VFR:
  13.         if (delta <= -0.6)
  14.             nb_frames = 0;
  15.         else if (delta > 0.6)
  16.             ost->sync_opts = lrint(sync_ipts);
  17.         break;
  18.     case VSYNC_DROP:
  19.     case VSYNC_PASSTHROUGH:
  20.         ost->sync_opts = lrint(sync_ipts);
  21.         break;
  22.     default:
  23.         av_assert0(0);
  24.     }

复制代码
这样来判断的

回复 支持 反对

使用道具 举报

发表于 2013-9-30 14:54:20 | 显示全部楼层
Mark一下
回复 支持 反对

使用道具 举报

发表于 2013-9-30 16:17:37 | 显示全部楼层
mark
回复 支持 反对

使用道具 举报

发表于 2013-10-1 10:38:04 | 显示全部楼层
MARK.
回复

使用道具 举报

发表于 2014-1-27 15:32:19 | 显示全部楼层
音视频同步的处理没看太明白
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|Archiver|ChinaFFmpeg

GMT+8, 2024-5-1 02:01 , Processed in 0.081134 second(s), 23 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表