天下之事,必先处之难,而后易之。

录像音视频同步原理分析及PTS计算公式

图解分析

音视频同步要分别保证开始的PTS一样,PTS是控制帧的显示时间的,所以要实现音视频同步必须分别设置音视频的PTS。


注:音、视频最后一帧的PTS时刻不一定相同。

1. 视频时间戳计算

pts = count++ *(1000/fps);  //其中count初始值为0,每次打完时间戳count加1.

//在ffmpeg,中的代码为
pkt.pts= count++ * (Ctx->time_base.num * 1000 / Ctx->time_base.den);

2. 音频时间戳

    

pts = count++ * (frame_size * 1000 / sample_rate)

//在ffmpeg中的代码为
pkt.pts= count++ * (Ctx->frame_size * 1000 / Ctx->sample_rate);


FFmpeg例子

视频:

 // 视频帧的播放时间依赖pts字段,音频和视频都有自己单独的pts
            // 音视频同步要以视频或音频为参考标准,然后控制延时来保证音视频的同步,最简单的是以音频为准同步视频
            // 录音编码时pts是一定的,由编码器以相同频率写入,音视频录制时总时长是一定的,都在同一个时间坐标序列上,但长度不一定一样
            // 计算音视频pts,都是根据每秒算出有多少个音视频帧, 然后根据time_base,算出每帧占有多少个时间单位,即为pts差值
            long pts = 0;

            // 计算视频公式: pkt.pts = count++ * (Ctx->time_base.num * 1000 / Ctx->time_base.den);
            //pts = (codec.Framecnt - 1) * (codec.GetCodecCtx()->pkt_timebase.num * 1000 / codec.GetCodecCtx()->pkt_timebase.den);
            pts = (codec.Framecnt - 1) * (codec.GetCodecCtx()->pkt_timebase.num * 1000 / 25);
            Console.WriteLine("...........video...pts:" + pts);
            // 初始化音视频共同的timer
            CvNetVideo.Play.AVPtsTimer.Init();
            // 调整时间戳与时间轴跑过的timer之间的延时
            while (CvNetVideo.Play.AVPtsTimer.Timer<pts)
            {
                Thread.Sleep(1);
                CvNetVideo.Play.AVPtsTimer.Sleep_Count++;
                Console.WriteLine("...........video...sleep count:" + CvNetVideo.Play.AVPtsTimer.Sleep_Count);
            }

音频:

 #region 设置音频的pts
                // 计算音频PTS公式:pkt.pts= count++ * (Ctx->frame_size * 1000 / Ctx->sample_rate);
               
                long pts = frame_count++ * (output_codec_context->frame_size * 1000 / output_codec_context->sample_rate);
                // 初始化音视频共同的timer
                CvNetVideo.Play.AVPtsTimer.Init();
                // 调整时间戳与时间轴跑过的timer之间的延时
                while (CvNetVideo.Play.AVPtsTimer.Timer < pts)
                {
                    Thread.Sleep(1);
                    CvNetVideo.Play.AVPtsTimer.Sleep_Count++;
                    Console.WriteLine("...........audio...sleep count:" + CvNetVideo.Play.AVPtsTimer.Sleep_Count);
                }
                frame->pts = pts;
                frame->pkt_dts = pts;
                #endregion
  
                Console.WriteLine("...........audio...pts:"  + pts);

Timer时间戳计算:

 /// <summary>
    /// PTS-Timer
    /// </summary>
    public class AVPtsTimer
    {
        /// <summary>
        /// 初始化时的时间戳
        /// </summary>
        private static long Start_Stamp { get; set; }

        public static bool IsInit = false;

        /// <summary>
        /// 休眠次数
        /// </summary>
        public static long Sleep_Count { get; set; }

        /// <summary>
        /// 时间轴跑过的时间
        /// </summary>
        public static long Timer { get { return ConvertDataTimeToLong(DateTime.Now) - Start_Stamp; } }

        public static void Init()
        {
            if (IsInit)
            {
                return ;
            }
            Start_Stamp = ConvertDataTimeToLong(DateTime.Now);
            IsInit = true;
        }

        /// <summary>
        /// 计算时间戳
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static long ConvertDataTimeToLong(DateTime dt)
        {
            DateTime dtStart = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
            TimeSpan toNow = dt.Subtract(dtStart);
            long timeStamp = toNow.Ticks;
            timeStamp = long.Parse(timeStamp.ToString().Substring(0, timeStamp.ToString().Length - 4));
            return timeStamp;
        }
    }

注:音视频同步仍然存在瑕疵,时间戳和延时已处理,但视频还是存在不同步的情况,应该是计算过程中直接使用的每秒25帧的值的问题,具体是多少还得再看。

上文中的代码是可以执行的但是中间的值是有问题的,代码如下:

pts = (codec.Framecnt - 1) * (codec.GetCodecCtx()->pkt_timebase.num * 1000 / codec.GetCodecCtx()->pkt_timebase.den);

int den=codec.GetCodecCtx()->pkt_timebase.den;

den=12800;//导致执行的时候pts一直为0,所以才改为的25帧每秒(25帧是我代码中视频录制的默认值)。


posted @ 2024-09-23 14:23  boonya  阅读(224)  评论(0)    收藏  举报  来源
我有佳人隔窗而居,今有伊人明月之畔。
轻歌柔情冰壶之浣,涓涓清流梦入云端。
美人如娇温雅悠婉,目遇赏阅适而自欣。
百草层叠疏而有致,此情此思怀彼佳人。
念所思之唯心叩之,踽踽彳亍寤寐思之。
行云如风逝而复归,佳人一去莫知可回?
深闺冷瘦独自徘徊,处处明灯影还如只。
推窗见月疑是归人,阑珊灯火托手思忖。
庐居闲客而好品茗,斟茶徐徐漫漫生烟。

我有佳人在水之畔,瓮载渔舟浣纱归还。
明月相照月色还低,浅近芦苇深深如钿。
庐山秋月如美人衣,画堂春阁香气靡靡。
秋意幽笃残粉摇曳,轻轻如诉画中蝴蝶。
泾水潺潺取尔浇园,暮色黄昏如沐佳人。
青丝撩弄长裙翩翩,彩蝶飞舞执子手腕。
香带丝缕缓缓在肩,柔美体肤寸寸爱怜。
如水之殇美玉成欢,我有佳人清新如兰。
伊人在水我在一边,远远相望不可亵玩。