You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
338 lines
372 KiB
338 lines
372 KiB
5 years ago
|
<!DOCTYPE html>
|
||
|
<!-- saved from url=(0051)https://cloud.tencent.com/developer/article/1409507 -->
|
||
|
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><title>FFmpeg时间戳详解 - 云+社区 - 腾讯云</title><meta name="keywords" content="图像处理"><meta name="description" content="I帧:I帧(Intra-coded picture, 帧内编码帧,常称为关键帧)包含一幅完整的图像信息,属于帧内编码图像,不含运动矢量,在解码时不需要参考其他帧..."><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, viewport-fit=cover"><meta name="format-detection" content="telephone=no"><link rel="stylesheet" href="./FFmpeg时间戳详解 - 云+社区 - 腾讯云_files/global-201911291451.css"><link rel="stylesheet" href="./FFmpeg时间戳详解 - 云+社区 - 腾讯云_files/AskDialog-201906131103.css"><link rel="stylesheet" href="./FFmpeg时间戳详解 - 云+社区 - 腾讯云_files/base-201911131148.css"><link rel="stylesheet" href="./FFmpeg时间戳详解 - 云+社区 - 腾讯云_files/Column-20199161611.css"><link rel="stylesheet" href="./FFmpeg时间戳详解 - 云+社区 - 腾讯云_files/markdown-201810241044.css"><link rel="stylesheet" href="./FFmpeg时间戳详解 - 云+社区 - 腾讯云_files/draft-master-v1.3.38.css"><style media="screen">@supports (padding:max(0px)){.set-safe-area .com-main{bottom:max(12px,constant(safe-area-inset-bottom));bottom:max(12px,env(safe-area-inset-bottom))}.set-safe-area .com-main-simple-sec{bottom:max(12px,constant(safe-area-inset-bottom));bottom:max(12px,env(safe-area-inset-bottom))}.set-safe-area .com-main-sec{bottom:max(12px,constant(safe-area-inset-bottom));bottom:max(12px,env(safe-area-inset-bottom))}.set-safe-area .com-m-footer{bottom:max(12px,constant(safe-area-inset-bottom));bottom:max(12px,env(safe-area-inset-bottom))}.set-safe-area .com-mobile-body{bottom:max(12px,constant(safe-area-inset-bottom));bottom:max(12px,env(safe-area-inset-bottom))}}@supports (padding:max(0px)){.set-safe-area .support-wrap,.set-safe-area div.body{bottom:max(12px,constant(safe-area-inset-bottom));bottom:max(12px,env(safe-area-inset-bottom))}.set-safe-area .com-responsive-no-ft div.body{bottom:max(12px,constant(safe-area-inset-bottom));bottom:max(12px,env(safe-area-inset-bottom))}}</style><style id="dynamic-inserted-css-transition-slide391298858">
|
||
|
.transition-slide391298858-enter {
|
||
|
opacity: 0 !important;
|
||
|
transform: translate3d(0px, -44px, 0);
|
||
|
}
|
||
|
.transition-slide391298858-enter-active {
|
||
|
opacity: 1 !important;
|
||
|
transform: translate3d(0, 0, 0);
|
||
|
transition: opacity 300ms ease, transform 300ms ease;
|
||
|
}
|
||
|
.transition-slide391298858-leave {
|
||
|
opacity: 1 !important;
|
||
|
transform: translate3d(0, 0, 0);
|
||
|
}
|
||
|
.transition-slide391298858-leave-active {
|
||
|
opacity: 0 !important;
|
||
|
transform: translate3d(0px, -44px, 0);
|
||
|
transition: opacity 300ms ease, transform 300ms ease;
|
||
|
}
|
||
|
</style><style id="dynamic-inserted-css-transition-fade-312660078">
|
||
|
.transition-fade-312660078-enter {
|
||
|
opacity: 0 !important;
|
||
|
}
|
||
|
.transition-fade-312660078-enter-active {
|
||
|
opacity: 0.7 !important;
|
||
|
transition: opacity 300ms ease;
|
||
|
}
|
||
|
.transition-fade-312660078-leave {
|
||
|
opacity: 0.7 !important;
|
||
|
}
|
||
|
.transition-fade-312660078-leave-active {
|
||
|
opacity: 0 !important;
|
||
|
transition: opacity 300.0001ms ease;
|
||
|
}
|
||
|
</style><style id="dynamic-inserted-css-transition-slide-1496561436">
|
||
|
.transition-slide-1496561436-enter {
|
||
|
opacity: 0 !important;
|
||
|
transform: translate3d(0px, -30px, 0);
|
||
|
}
|
||
|
.transition-slide-1496561436-enter-active {
|
||
|
opacity: 1 !important;
|
||
|
transform: translate3d(0, 0, 0);
|
||
|
transition: opacity 300ms ease, transform 300ms ease;
|
||
|
}
|
||
|
.transition-slide-1496561436-leave {
|
||
|
opacity: 1 !important;
|
||
|
transform: translate3d(0, 0, 0);
|
||
|
}
|
||
|
.transition-slide-1496561436-leave-active {
|
||
|
opacity: 0 !important;
|
||
|
transform: translate3d(0px, -30px, 0);
|
||
|
transition: opacity 300ms ease, transform 300ms ease;
|
||
|
}
|
||
|
</style><script type="text/javascript" async="" src="./FFmpeg时间戳详解 - 云+社区 - 腾讯云_files/f.txt"></script><script type="text/javascript" async="" src="./FFmpeg时间戳详解 - 云+社区 - 腾讯云_files/f.txt"></script><script async="" src="./FFmpeg时间戳详解 - 云+社区 - 腾讯云_files/gtm.js.下载"></script><script async="" src="./FFmpeg时间戳详解 - 云+社区 - 腾讯云_files/analytics.js.下载"></script><script src="./FFmpeg时间戳详解 - 云+社区 - 腾讯云_files/stats"></script><script src="./FFmpeg时间戳详解 - 云+社区 - 腾讯云_files/stats.js.下载" name="MTAH5" sid="500505369" cid="500505370"></script><script src="./FFmpeg时间戳详解 - 云+社区 - 腾讯云_files/index.js.下载"></script><script type="text/javascript" async="" src="./FFmpeg时间戳详解 - 云+社区 - 腾讯云_files/MathJax.js.下载"></script><style id="dynamic-inserted-css-article-sharing!.extra-cnt">.extra-cnt .com-share-options .qr-img img {
|
||
|
width: 100px;
|
||
|
height: 100px;
|
||
|
}</style><style id="dynamic-inserted-css-transition-slide-1218967474">
|
||
|
.transition-slide-1218967474-enter {
|
||
|
opacity: 0 !important;
|
||
|
transform: translate3d(20px, 0px, 0);
|
||
|
}
|
||
|
.transition-slide-1218967474-enter-active {
|
||
|
opacity: 1 !important;
|
||
|
transform: translate3d(0, 0, 0);
|
||
|
transition: opacity 300ms ease, transform 300ms ease;
|
||
|
}
|
||
|
.transition-slide-1218967474-leave {
|
||
|
opacity: 1 !important;
|
||
|
transform: translate3d(0, 0, 0);
|
||
|
}
|
||
|
.transition-slide-1218967474-leave-active {
|
||
|
opacity: 0 !important;
|
||
|
transform: translate3d(20px, 0px, 0);
|
||
|
transition: opacity 300ms ease, transform 300ms ease;
|
||
|
}
|
||
|
</style><script src="./FFmpeg时间戳详解 - 云+社区 - 腾讯云_files/left-nav.3cab8319a893bdfb2876e4cd5499b986.js.下载"></script><script src="./FFmpeg时间戳详解 - 云+社区 - 腾讯云_files/index.93e5437c205358f68660.js.下载"></script><style type="text/css">.MathJax_Preview {color: #888}
|
||
|
#MathJax_Message {position: fixed; left: 1em; bottom: 1.5em; background-color: #E6E6E6; border: 1px solid #959595; margin: 0px; padding: 2px 8px; z-index: 102; color: black; font-size: 80%; width: auto; white-space: nowrap}
|
||
|
#MathJax_MSIE_Frame {position: absolute; top: 0; left: 0; width: 0px; z-index: 101; border: 0px; margin: 0px; padding: 0px}
|
||
|
.MathJax_Error {color: #CC0000; font-style: italic}
|
||
|
</style><script src="./FFmpeg时间戳详解 - 云+社区 - 腾讯云_files/f(1).txt"></script><script src="./FFmpeg时间戳详解 - 云+社区 - 腾讯云_files/f(2).txt"></script></head><body><div id="MathJax_Message" style="display: none;"></div><div id="react-root"><div class=""><div class="J-header c-nav-wrap c-nav com-2-nav fixed"><div class="c-nav-mod c-nav-mod-pc c-nav-white"><div id="qcTopNavFstLevel" class="J-headerTop c-nav-top" data-nosearch="1" data-nolang="1">
|
||
|
<div style="position:absolute;left:20px;top:0px;">
|
||
|
|
||
|
<a id="qcLeftNavSwitcher" href="javascript:;" class="c-nav-all-trigger" hotrep="hp.header.trigger" aria-haspopup="true" aria-expanded="false" role="button" aria-controls="qcLeftNavSwitcher">
|
||
|
<i class="c-nav-all-trigger-icon"></i>
|
||
|
<span class="c-nav-all-trigger-text">菜单</span>
|
||
|
</a>
|
||
|
<i class="c-nav-top-divider"></i>
|
||
|
|
||
|
<a class="c-nav-bg-logo" href="https://cloud.tencent.com/" hotrep="hp.header.fst.logo">
|
||
|
<i>腾讯云</i>
|
||
|
</a>
|
||
|
</div>
|
||
|
<div class="c-nav-top-operation">
|
||
|
|
||
|
|
||
|
<div class="c-nav-entry-list">
|
||
|
<ul>
|
||
|
<li class="c-nav-entry-item">
|
||
|
<a href="https://cloud.tencent.com/" target="_blank" hotrep="hp.header.fst.home">腾讯云首页</a>
|
||
|
</li>
|
||
|
</ul>
|
||
|
</div>
|
||
|
|
||
|
<div class="c-nav-top-ctrl">
|
||
|
|
||
|
<div class="c-nav-top-links">
|
||
|
<a href="https://cloud.tencent.com/product/ba" hotrep="hp.header.ba" class="link-item">备案</a>
|
||
|
<a href="https://console.cloud.tencent.com/" hotrep="hp.header.console" class="link-item">控制台</a>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div></div><div class="J-headerBottom c-nav-bottom responsive" style="width: 1522.4px; left: 0px;"><a href="https://cloud.tencent.com/developer" class="c-nav-project-logo" hotrep="community.header.logo.community"><i class="c-nav-project-bg-logo">云+社区</i></a><div class="c-nav-bm-categories"><ul class="c-nav-list"><li class="presentation curr"><a href="https://cloud.tencent.com/developer/column" role="button" hotrep="community.header.nav.column">专栏</a></li><li class="presentation"><a href="https://cloud.tencent.com/developer/ask" role="button" hotrep="community.header.nav.ask">问答</a></li><li class="presentation"><a href="https://cloud.tencent.com/developer/salon" role="button" hotrep="community.header.nav.salon">沙龙</a></li><li class="presentation"><a href="https://cloud.tencent.com/developer/news" role="button" hotrep="community.header.nav.news">快讯</a></li><li class="presentation"><a href="https://cloud.tencent.com/developer/teams" role="button" hotrep="community.header.nav.teams">团队主页</a></li><li class="presentation"><a href="https://cloud.tencent.com/developer/devdocs" role="button" hotrep="community.header.nav.devdocs">开发者手册</a></li><li class="presentation"><a href="https://cloud.tencent.com/developer/timl" role="button" hotrep="community.header.nav.timl">智能钛AI</a></li><li class="presentation split-item"></li><li class="presentation"><a href="https://cloud.tencent.com/edu/learning?ADTAG=sq.bar" target="_blank" role="button" hotrep="community.header.nav.edu">在线学习中心</a></li><li class="presentation"><a href="https://cloud.tencent.com/tvp" target="_blank" role="button" hotrep="community.header.nav.tvp">TVP</a></li></ul></div><div class="J-headerBottomRight c-nav-bm-right"><div class="c-nav-bm-search-wrap"><a href="javascript:;" class="c-nav-bm-search-btn" role="button" hotrep="community.header.opensearch.search_icon"><i class="nav-search-icon"></i> 搜索</a><div class="c-nav-bm-search search-icon-rt" role="button" hotrep="community.header.opensearch.search_bar"><input type="text" placeholder="找文章 / 找答案 / 找技术大牛" readonly="" style="cursor:pointer"><i class="nav-search-icon"></i></div><div class="com-3-nav-search-wrap"><div class="com-search-bar"><label class="com-search"><input type="text" class="search-input" value="" placeholder="找文章 / 找答案 / 找技术大牛" autocomplete="off"><button type="submit" class="search-btn" hotrep="community.header.search.search_btn">搜索</button><button type="button" class="close-btn" hotrep="community.header.search.close_btn">关闭</button></label></div></div></div><div class="c-nav-bm-btns"><a href="javascript:;" class="c-btn c-nav-bm-btn" hotrep="community.header.bm_btns.write_article">写文章</a><a href="javascript:;" class="c-btn c-nav-bm-btn-hole" hotrep="community.header.bm_btns.ask">提问</a></div><div class="c-nav-bm-text-btns"><a href="javascript:;" class="c-nav-bm-text-btn" hotrep="community.header.login">登录</a><i class="c-nav-divider-dot"></i><a href="https://cloud.tencent.com/register" target="_blank" class="c-nav-bm-text-btn" hotrep="community.header.register">注册</a></div></div><div class="c-nav-pro-mask" style="display:none"></div></div></div></div><div class="com-header subsite"><div class="header-main"><a href="javascript:;" class="header-menu-trigger" hotrep="community.header.menu_trigger">展开</a><h1 class="header-main-title"></h1></div><div class="header-logo-wrap"><div class="header-logo"><i class="logo-icon logo-community"></i><h1 class="header-title">腾讯云·社区</h1></div></div><nav class="header-extra"><a href="https://cloud.tencent.com/developer/search" class="header-extra-icon search-entry" hotrep="community.header.search_btn"></a><a href="javascript:;" hotrep="community.header.login">登录</a></nav></div><div class="com-m-side-mask hidden"></div><section class="com-m-side com-2-m-side hidden"><header class="com-m-side-hd"><div class="header-logo-wrap"><a href="https://cloud.tencent.com/developer" class="header-logo" hotrep="community.header.drawer.hom
|
||
|
B帧具有更高的压缩率,但需要更多的缓冲时间以及更高的CPU占用率,因此B帧适合本地存储以及<a data-text-link="49_1409507" data-from="10680" href="https://cloud.tencent.com/product/vod?from=10680" target="_blank">视频点播</a>,而不适用对实时性要求较高的直播系统。</p><h2 id="2.-DTS%E5%92%8CPTS" name="2.-DTS%E5%92%8CPTS">2. DTS和PTS</h2><p>DTS(Decoding Time Stamp, 解码时间戳),表示压缩帧的解码时间。
|
||
|
PTS(Presentation Time Stamp, 显示时间戳),表示将压缩帧解码后得到的原始帧的显示时间。
|
||
|
音频中DTS和PTS是相同的。视频中由于B帧需要双向预测,B帧依赖于其前和其后的帧,因此含B帧的视频解码顺序与显示顺序不同,即DTS与PTS不同。当然,不含B帧的视频,其DTS和PTS是相同的。下图以一个开放式GOP示意图为例,说明视频流的解码顺序和显示顺序
|
||
|
</p><figure><div class="image-block"><span><img src="./FFmpeg时间戳详解 - 云+社区 - 腾讯云_files/lv0b2oqwtf.jpeg" class="" style="cursor: zoom-in;"></span></div></figure><p>
|
||
|
<strong>采集顺序</strong>指图像传感器采集原始信号得到图像帧的顺序。
|
||
|
<strong>编码顺序</strong>指编码器编码后图像帧的顺序。存储到磁盘的本地视频文件中图像帧的顺序与编码顺序相同。
|
||
|
<strong>传输顺序</strong>指编码后的流在网络中传输过程中图像帧的顺序。
|
||
|
<strong>解码顺序</strong>指解码器解码图像帧的顺序。
|
||
|
<strong>显示顺序</strong>指图像帧在显示器上显示的顺序。
|
||
|
<strong>采集顺序与显示顺序相同。编码顺序、传输顺序和解码顺序相同。</strong>
|
||
|
以图中“B[1]”帧为例进行说明,“B[1]”帧解码时需要参考“I[0]”帧和“P[3]”帧,因此“P[3]”帧必须比“B[1]”帧先解码。这就导致了解码顺序和显示顺序的不一致,后显示的帧需要先解码。</p><h2 id="3.-FFmpeg%E4%B8%AD%E7%9A%84%E6%97%B6%E9%97%B4%E5%9F%BA%E4%B8%8E%E6%97%B6%E9%97%B4%E6%88%B3" name="3.-FFmpeg%E4%B8%AD%E7%9A%84%E6%97%B6%E9%97%B4%E5%9F%BA%E4%B8%8E%E6%97%B6%E9%97%B4%E6%88%B3">3. FFmpeg中的时间基与时间戳</h2><h3 id="3.1-%E6%97%B6%E9%97%B4%E5%9F%BA%E4%B8%8E%E6%97%B6%E9%97%B4%E6%88%B3%E7%9A%84%E6%A6%82%E5%BF%B5" name="3.1-%E6%97%B6%E9%97%B4%E5%9F%BA%E4%B8%8E%E6%97%B6%E9%97%B4%E6%88%B3%E7%9A%84%E6%A6%82%E5%BF%B5">3.1 时间基与时间戳的概念</h3><p>在FFmpeg中,时间基(time_base)是时间戳(timestamp)的单位,时间戳值乘以时间基,可以得到实际的时刻值(以秒等为单位)。例如,如果一个视频帧的dts是40,pts是160,其time_base是1/1000,那么可以计算出此视频帧的解码时刻是40毫秒(40/1000),显示时刻是160毫秒(160/1000)。FFmpeg中时间戳(pts/dts)的类型是int64_t类型,把一个time_base看作一个时钟脉冲,则可把dts/pts看作时钟脉冲的计数。</p><h3 id="3.2-%E4%B8%89%E7%A7%8D%E6%97%B6%E9%97%B4%E5%9F%BAtbr%E3%80%81tbn%E5%92%8Ctbc" name="3.2-%E4%B8%89%E7%A7%8D%E6%97%B6%E9%97%B4%E5%9F%BAtbr%E3%80%81tbn%E5%92%8Ctbc">3.2 三种时间基tbr、tbn和tbc</h3><p>不同的封装格式具有不同的时间基。在FFmpeg处理音视频过程中的不同阶段,也会采用不同的时间基。
|
||
|
FFmepg中有三种时间基,命令行中tbr、tbn和tbc的打印值就是这三种时间基的倒数:
|
||
|
tbn:对应容器中的时间基。值是AVStream.time_base的倒数
|
||
|
tbc:对应编解码器中的时间基。值是AVCodecContext.time_base的倒数
|
||
|
tbr:从视频流中猜算得到,可能是帧率或场率(帧率的2倍)</p><p>测试文件下载:<a data-from="10680" href="https://github.com/leichn/blog_resources/blob/master/video/tuan/tnmil3.flv" target="_blank" rel="nofollow noopener noreferrer">tnmil3.flv</a>
|
||
|
使用ffprobe探测媒体文件格式,如下:</p><pre class="prism-token token language-javascript">think@opensuse<span class="token operator">></span> ffprobe tnmil3<span class="token punctuation">.</span>flv
|
||
|
ffprobe version <span class="token number">4.1</span> <span class="token function">Copyright</span> <span class="token punctuation">(</span>c<span class="token punctuation">)</span> <span class="token number">2007</span><span class="token operator">-</span><span class="token number">2018</span> the FFmpeg developers
|
||
|
Input #<span class="token number">0</span><span class="token punctuation">,</span> flv<span class="token punctuation">,</span> <span class="token keyword">from</span> <span class="token string">'tnmil3.flv'</span><span class="token punctuation">:</span>
|
||
|
Metadata<span class="token punctuation">:</span>
|
||
|
encoder <span class="token punctuation">:</span> Lavf58<span class="token number">.20</span><span class="token punctuation">.</span><span class="token number">100</span>
|
||
|
Duration<span class="token punctuation">:</span> <span class="token number">00</span><span class="token punctuation">:</span><span class="token number">00</span><span class="token punctuation">:</span><span class="token number">03.60</span><span class="token punctuation">,</span> start<span class="token punctuation">:</span> <span class="token number">0.017000</span><span class="token punctuation">,</span> bitrate<span class="token punctuation">:</span> <span class="token number">513</span> kb<span class="token operator">/</span>s
|
||
|
Stream #<span class="token number">0</span><span class="token punctuation">:</span><span class="token number">0</span><span class="token punctuation">:</span> Video<span class="token punctuation">:</span> <span class="token function">h264</span> <span class="token punctuation">(</span>High<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">yuv420p</span><span class="token punctuation">(</span>progressive<span class="token punctuation">)</span><span class="token punctuation">,</span> 784x480<span class="token punctuation">,</span> <span class="token number">25</span> fps<span class="token punctuation">,</span> <span class="token number">25</span> tbr<span class="token punctuation">,</span> 1k tbn<span class="token punctuation">,</span> <span class="token number">50</span> tbc
|
||
|
Stream #<span class="token number">0</span><span class="token punctuation">:</span><span class="token number">1</span><span class="token punctuation">:</span> Audio<span class="token punctuation">:</span> <span class="token function">aac</span> <span class="token punctuation">(</span>LC<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">44100</span> Hz<span class="token punctuation">,</span> stereo<span class="token punctuation">,</span> fltp<span class="token punctuation">,</span> <span class="token number">128</span> kb<span class="token operator">/</span>s</pre><p>关于tbr、tbn和tbc的说明,原文如下,来自FFmpeg邮件列表:</p><blockquote><p> There are three different time bases for time stamps in FFmpeg. The
|
||
|
values printed are actually reciprocals of these, i.e. 1/tbr, 1/tbn and
|
||
|
1/tbc.
|
||
|
tbn is the time base in AVStream that has come from the container, I
|
||
|
think. It is used for all AVStream time stamps.
|
||
|
tbc is the time base in AVCodecContext for the codec used for a
|
||
|
particular stream. It is used for all AVCodecContext and related time
|
||
|
stamps.
|
||
|
tbr is guessed from the video stream and is the value users want to see
|
||
|
when they look for the video frame rate, except sometimes it is twice
|
||
|
what one would expect because of field rate versus frame rate.
|
||
|
</p></blockquote><h3 id="3.3-%E5%86%85%E9%83%A8%E6%97%B6%E9%97%B4%E5%9F%BAAV_TIME_BASE" name="3.3-%E5%86%85%E9%83%A8%E6%97%B6%E9%97%B4%E5%9F%BAAV_TIME_BASE">3.3 内部时间基AV_TIME_BASE</h3><p>除以上三种时间基外,FFmpeg还有一个内部时间基AV_TIME_BASE(以及分数形式的AV_TIME_BASE_Q)</p><pre class="prism-token token language-javascript"><span class="token comment">// Internal time base represented as integer</span>
|
||
|
#define AV_TIME_BASE <span class="token number">1000000</span>
|
||
|
|
||
|
<span class="token comment">// Internal time base represented as fractional value</span>
|
||
|
#define <span class="token function">AV_TIME_BASE_Q</span> <span class="token punctuation">(</span>AVRational<span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token number">1</span><span class="token punctuation">,</span> AV_TIME_BASE<span class="token punctuation">}</span></pre><p>AV_TIME_BASE及AV_TIME_BASE_Q用于FFmpeg内部函数处理,使用此时间基计算得到时间值表示的是微秒。</p><h3 id="3.4-%E6%97%B6%E9%97%B4%E5%80%BC%E5%BD%A2%E5%BC%8F%E8%BD%AC%E6%8D%A2" name="3.4-%E6%97%B6%E9%97%B4%E5%80%BC%E5%BD%A2%E5%BC%8F%E8%BD%AC%E6%8D%A2">3.4 时间值形式转换</h3><p>av_q2d()将时间从AVRational形式转换为double形式。AVRational是分数类型,double是双精度浮点数类型,转换的结果单位是秒。转换前后的值基于同一时间基,仅仅是数值的表现形式不同而已。</p><p>qv_q2d()实现如下:</p><pre class="prism-token token language-javascript"><span class="token comment">/**
|
||
|
* Convert an AVRational to a `double`.
|
||
|
* @param a AVRational to convert
|
||
|
* @return `a` in floating-point form
|
||
|
* @see av_d2q()
|
||
|
*/</span>
|
||
|
<span class="token keyword">static</span> inline double <span class="token function">av_q2d</span><span class="token punctuation">(</span>AVRational a<span class="token punctuation">)</span><span class="token punctuation">{</span>
|
||
|
<span class="token keyword">return</span> a<span class="token punctuation">.</span>num <span class="token operator">/</span> <span class="token punctuation">(</span>double<span class="token punctuation">)</span> a<span class="token punctuation">.</span>den<span class="token punctuation">;</span>
|
||
|
<span class="token punctuation">}</span></pre><p>qv_q2d()使用方法如下:</p><pre class="prism-token token language-javascript"><span class="token function">时刻值:timestamp</span><span class="token punctuation">(</span>单位秒<span class="token punctuation">)</span> <span class="token operator">=</span> pts × <span class="token function">av_q2d</span><span class="token punctuation">(</span>stream<span class="token operator">-</span><span class="token operator">></span>time_base<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
<span class="token function">时长值:duration</span><span class="token punctuation">(</span>单位秒<span class="token punctuation">)</span> <span class="token operator">=</span> stream<span class="token operator">-</span><span class="token operator">></span>duration × <span class="token function">av_q2d</span><span class="token punctuation">(</span>stream<span class="token operator">-</span><span class="token operator">></span>time_base<span class="token punctuation">)</span><span class="token punctuation">;</span></pre><h3 id="3.5-%E6%97%B6%E9%97%B4%E5%9F%BA%E8%BD%AC%E6%8D%A2%E5%87%BD%E6%95%B0" name="3.5-%E6%97%B6%E9%97%B4%E5%9F%BA%E8%BD%AC%E6%8D%A2%E5%87%BD%E6%95%B0">3.5 时间基转换函数</h3><p>av_rescale_q()用于不同时间基的转换,用于将时间值从一种时间基转换为另一种时间基。</p><pre class="prism-token token language-javascript"><span class="token comment">/**
|
||
|
* Rescale a 64-bit integer by 2 rational numbers.
|
||
|
*
|
||
|
* The operation is mathematically equivalent to `a × bq / cq`.
|
||
|
*
|
||
|
* This function is equivalent to av_rescale_q_rnd() with #AV_ROUND_NEAR_INF.
|
||
|
*
|
||
|
* @see av_rescale(), av_rescale_rnd(), av_rescale_q_rnd()
|
||
|
*/</span>
|
||
|
int64_t <span class="token function">av_rescale_q</span><span class="token punctuation">(</span>int64_t a<span class="token punctuation">,</span> AVRational bq<span class="token punctuation">,</span> AVRational cq<span class="token punctuation">)</span> av_const<span class="token punctuation">;</span></pre><p>av_packet_rescale_ts()用于将AVPacket中各种时间值从一种时间基转换为另一种时间基。</p><pre class="prism-token token language-javascript"><span class="token comment">/**
|
||
|
* Convert valid timing fields (timestamps / durations) in a packet from one
|
||
|
* timebase to another. Timestamps with unknown values (AV_NOPTS_VALUE) will be
|
||
|
* ignored.
|
||
|
*
|
||
|
* @param pkt packet on which the conversion will be performed
|
||
|
* @param tb_src source timebase, in which the timing fields in pkt are
|
||
|
* expressed
|
||
|
* @param tb_dst destination timebase, to which the timing fields will be
|
||
|
* converted
|
||
|
*/</span>
|
||
|
<span class="token keyword">void</span> <span class="token function">av_packet_rescale_ts</span><span class="token punctuation">(</span>AVPacket <span class="token operator">*</span>pkt<span class="token punctuation">,</span> AVRational tb_src<span class="token punctuation">,</span> AVRational tb_dst<span class="token punctuation">)</span><span class="token punctuation">;</span></pre><h3 id="3.6-%E8%BD%AC%E5%B0%81%E8%A3%85%E8%BF%87%E7%A8%8B%E4%B8%AD%E7%9A%84%E6%97%B6%E9%97%B4%E5%9F%BA%E8%BD%AC%E6%8D%A2" name="3.6-%E8%BD%AC%E5%B0%81%E8%A3%85%E8%BF%87%E7%A8%8B%E4%B8%AD%E7%9A%84%E6%97%B6%E9%97%B4%E5%9F%BA%E8%BD%AC%E6%8D%A2">3.6 转封装过程中的时间基转换</h3><p>容器中的时间基(AVStream.time_base,3.2节中的tbn)定义如下:</p><pre class="prism-token token language-javascript">typedef struct AVStream <span class="token punctuation">{</span>
|
||
|
<span class="token operator">...</span><span class="token operator">...</span>
|
||
|
<span class="token comment">/**
|
||
|
* This is the fundamental unit of time (in seconds) in terms
|
||
|
* of which frame timestamps are represented.
|
||
|
*
|
||
|
* decoding: set by libavformat
|
||
|
* encoding: May be set by the caller before avformat_write_header() to
|
||
|
* provide a hint to the muxer about the desired timebase. In
|
||
|
* avformat_write_header(), the muxer will overwrite this field
|
||
|
* with the timebase that will actually be used for the timestamps
|
||
|
* written into the file (which may or may not be related to the
|
||
|
* user-provided one, depending on the format).
|
||
|
*/</span>
|
||
|
AVRational time_base<span class="token punctuation">;</span>
|
||
|
<span class="token operator">...</span><span class="token operator">...</span>
|
||
|
<span class="token punctuation">}</span></pre><p>AVStream.time_base是AVPacket中pts和dts的时间单位,输入流与输出流中time_base按如下方式确定:
|
||
|
对于输入流:打开输入文件后,调用avformat_find_stream_info()可获取到每个流中的time_base
|
||
|
对于输出流:打开输出文件后,调用avformat_write_header()可根据输出文件封装格式确定每个流的time_base并写入输出文件中</p><p>不同封装格式具有不同的时间基,在转封装(将一种封装格式转换为另一种封装格式)过程中,时间基转换相关代码如下:</p><pre class="prism-token token language-javascript"><span class="token function">av_read_frame</span><span class="token punctuation">(</span>ifmt_ctx<span class="token punctuation">,</span> <span class="token operator">&</span>pkt<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
pkt<span class="token punctuation">.</span>pts <span class="token operator">=</span> <span class="token function">av_rescale_q_rnd</span><span class="token punctuation">(</span>pkt<span class="token punctuation">.</span>pts<span class="token punctuation">,</span> in_stream<span class="token operator">-</span><span class="token operator">></span>time_base<span class="token punctuation">,</span> out_stream<span class="token operator">-</span><span class="token operator">></span>time_base<span class="token punctuation">,</span> AV_ROUND_NEAR_INF<span class="token operator">|</span>AV_ROUND_PASS_MINMAX<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
pkt<span class="token punctuation">.</span>dts <span class="token operator">=</span> <span class="token function">av_rescale_q_rnd</span><span class="token punctuation">(</span>pkt<span class="token punctuation">.</span>dts<span class="token punctuation">,</span> in_stream<span class="token operator">-</span><span class="token operator">></span>time_base<span class="token punctuation">,</span> out_stream<span class="token operator">-</span><span class="token operator">></span>time_base<span class="token punctuation">,</span> AV_ROUND_NEAR_INF<span class="token operator">|</span>AV_ROUND_PASS_MINMAX<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
pkt<span class="token punctuation">.</span>duration <span class="token operator">=</span> <span class="token function">av_rescale_q</span><span class="token punctuation">(</span>pkt<span class="token punctuation">.</span>duration<span class="token punctuation">,</span> in_stream<span class="token operator">-</span><span class="token operator">></span>time_base<span class="token punctuation">,</span> out_stream<span class="token operator">-</span><span class="token operator">></span>time_base<span class="token punctuation">)</span><span class="token punctuation">;</span></pre><p>下面的代码具有和上面代码相同的效果:</p><pre class="prism-token token language-javascript"><span class="token comment">// 从输入文件中读取packet</span>
|
||
|
<span class="token function">av_read_frame</span><span class="token punctuation">(</span>ifmt_ctx<span class="token punctuation">,</span> <span class="token operator">&</span>pkt<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
<span class="token comment">// 将packet中的各时间值从输入流封装格式时间基转换到输出流封装格式时间基</span>
|
||
|
<span class="token function">av_packet_rescale_ts</span><span class="token punctuation">(</span><span class="token operator">&</span>pkt<span class="token punctuation">,</span> in_stream<span class="token operator">-</span><span class="token operator">></span>time_base<span class="token punctuation">,</span> out_stream<span class="token operator">-</span><span class="token operator">></span>time_base<span class="token punctuation">)</span><span class="token punctuation">;</span></pre><p>这里流里的时间基<code>in_stream->time_base</code>和<code>out_stream->time_base</code>,是容器中的时间基,就是3.2节中的tbn。</p><p>例如,flv封装格式的time_base为{1,1000},ts封装格式的time_base为{1,90000}
|
||
|
我们编写程序将flv封装格式转换为ts封装格式,抓取原文件(flv)的前四帧显示时间戳:</p><pre class="prism-token token language-javascript">think@opensuse<span class="token operator">></span> ffprobe <span class="token operator">-</span>show_frames <span class="token operator">-</span>select_streams v tnmil3<span class="token punctuation">.</span>flv <span class="token operator">|</span> grep pkt_pts
|
||
|
ffprobe version <span class="token number">4.1</span> <span class="token function">Copyright</span> <span class="token punctuation">(</span>c<span class="token punctuation">)</span> <span class="token number">2007</span><span class="token operator">-</span><span class="token number">2018</span> the FFmpeg developers
|
||
|
Input #<span class="token number">0</span><span class="token punctuation">,</span> flv<span class="token punctuation">,</span> <span class="token keyword">from</span> <span class="token string">'tnmil3.flv'</span><span class="token punctuation">:</span>
|
||
|
Metadata<span class="token punctuation">:</span>
|
||
|
encoder <span class="token punctuation">:</span> Lavf58<span class="token number">.20</span><span class="token punctuation">.</span><span class="token number">100</span>
|
||
|
Duration<span class="token punctuation">:</span> <span class="token number">00</span><span class="token punctuation">:</span><span class="token number">00</span><span class="token punctuation">:</span><span class="token number">03.60</span><span class="token punctuation">,</span> start<span class="token punctuation">:</span> <span class="token number">0.017000</span><span class="token punctuation">,</span> bitrate<span class="token punctuation">:</span> <span class="token number">513</span> kb<span class="token operator">/</span>s
|
||
|
Stream #<span class="token number">0</span><span class="token punctuation">:</span><span class="token number">0</span><span class="token punctuation">:</span> Video<span class="token punctuation">:</span> <span class="token function">h264</span> <span class="token punctuation">(</span>High<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">yuv420p</span><span class="token punctuation">(</span>progressive<span class="token punctuation">)</span><span class="token punctuation">,</span> 784x480<span class="token punctuation">,</span> <span class="token number">25</span> fps<span class="token punctuation">,</span> <span class="token number">25</span> tbr<span class="token punctuation">,</span> 1k tbn<span class="token punctuation">,</span> <span class="token number">50</span> tbc
|
||
|
Stream #<span class="token number">0</span><span class="token punctuation">:</span><span class="token number">1</span><span class="token punctuation">:</span> Audio<span class="token punctuation">:</span> <span class="token function">aac</span> <span class="token punctuation">(</span>LC<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">44100</span> Hz<span class="token punctuation">,</span> stereo<span class="token punctuation">,</span> fltp<span class="token punctuation">,</span> <span class="token number">128</span> kb<span class="token operator">/</span>s
|
||
|
pkt_pts<span class="token operator">=</span><span class="token number">80</span>
|
||
|
pkt_pts_time<span class="token operator">=</span><span class="token number">0.080000</span>
|
||
|
pkt_pts<span class="token operator">=</span><span class="token number">120</span>
|
||
|
pkt_pts_time<span class="token operator">=</span><span class="token number">0.120000</span>
|
||
|
pkt_pts<span class="token operator">=</span><span class="token number">160</span>
|
||
|
pkt_pts_time<span class="token operator">=</span><span class="token number">0.160000</span>
|
||
|
pkt_pts<span class="token operator">=</span><span class="token number">200</span>
|
||
|
pkt_pts_time<span class="token operator">=</span><span class="token number">0.200000</span></pre><p>再抓取转换的文件(ts)的前四帧显示时间戳:</p><pre class="prism-token token language-javascript">think@opensuse<span class="token operator">></span> ffprobe <span class="token operator">-</span>show_frames <span class="token operator">-</span>select_streams v tnmil3<span class="token punctuation">.</span>ts <span class="token operator">|</span> grep pkt_pts
|
||
|
ffprobe version <span class="token number">4.1</span> <span class="token function">Copyright</span> <span class="token punctuation">(</span>c<span class="token punctuation">)</span> <span class="token number">2007</span><span class="token operator">-</span><span class="token number">2018</span> the FFmpeg developers
|
||
|
Input #<span class="token number">0</span><span class="token punctuation">,</span> mpegts<span class="token punctuation">,</span> <span class="token keyword">from</span> <span class="token string">'tnmil3.ts'</span><span class="token punctuation">:</span>
|
||
|
Duration<span class="token punctuation">:</span> <span class="token number">00</span><span class="token punctuation">:</span><span class="token number">00</span><span class="token punctuation">:</span><span class="token number">03.58</span><span class="token punctuation">,</span> start<span class="token punctuation">:</span> <span class="token number">0.017000</span><span class="token punctuation">,</span> bitrate<span class="token punctuation">:</span> <span class="token number">619</span> kb<span class="token operator">/</span>s
|
||
|
Program <span class="token number">1</span>
|
||
|
Metadata<span class="token punctuation">:</span>
|
||
|
service_name <span class="token punctuation">:</span> Service01
|
||
|
service_provider<span class="token punctuation">:</span> FFmpeg
|
||
|
Stream #<span class="token number">0</span><span class="token punctuation">:</span><span class="token number">0</span><span class="token punctuation">[</span><span class="token number">0x100</span><span class="token punctuation">]</span><span class="token punctuation">:</span> Video<span class="token punctuation">:</span> <span class="token function">h264</span> <span class="token punctuation">(</span>High<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token number">27</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">/</span> <span class="token number">0x001B</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">yuv420p</span><span class="token punctuation">(</span>progressive<span class="token punctuation">)</span><span class="token punctuation">,</span> 784x480<span class="token punctuation">,</span> <span class="token number">25</span> fps<span class="token punctuation">,</span> <span class="token number">25</span> tbr<span class="token punctuation">,</span> 90k tbn<span class="token punctuation">,</span> <span class="token number">50</span> tbc
|
||
|
Stream #<span class="token number">0</span><span class="token punctuation">:</span><span class="token number">1</span><span class="token punctuation">[</span><span class="token number">0x101</span><span class="token punctuation">]</span><span class="token punctuation">:</span> Audio<span class="token punctuation">:</span> <span class="token function">aac</span> <span class="token punctuation">(</span>LC<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token number">15</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">/</span> <span class="token number">0x000F</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">44100</span> Hz<span class="token punctuation">,</span> stereo<span class="token punctuation">,</span> fltp<span class="token punctuation">,</span> <span class="token number">127</span> kb<span class="token operator">/</span>s
|
||
|
pkt_pts<span class="token operator">=</span><span class="token number">7200</span>
|
||
|
pkt_pts_time<span class="token operator">=</span><span class="token number">0.080000</span>
|
||
|
pkt_pts<span class="token operator">=</span><span class="token number">10800</span>
|
||
|
pkt_pts_time<span class="token operator">=</span><span class="token number">0.120000</span>
|
||
|
pkt_pts<span class="token operator">=</span><span class="token number">14400</span>
|
||
|
pkt_pts_time<span class="token operator">=</span><span class="token number">0.160000</span>
|
||
|
pkt_pts<span class="token operator">=</span><span class="token number">18000</span>
|
||
|
pkt_pts_time<span class="token operator">=</span><span class="token number">0.200000</span></pre><p>可以发现,对于同一个视频帧,它们时间基(tbn)不同因此时间戳(pkt_pts)也不同,但是计算出来的时刻值(pkt_pts_time)是相同的。
|
||
|
看第一帧的时间戳,计算关系:80×{1,1000} == 7200×{1,90000} == 0.080000</p><h3 id="3.7-%E8%BD%AC%E7%A0%81%E8%BF%87%E7%A8%8B%E4%B8%AD%E7%9A%84%E6%97%B6%E9%97%B4%E5%9F%BA%E8%BD%AC%E6%8D%A2" name="3.7-%E8%BD%AC%E7%A0%81%E8%BF%87%E7%A8%8B%E4%B8%AD%E7%9A%84%E6%97%B6%E9%97%B4%E5%9F%BA%E8%BD%AC%E6%8D%A2">3.7 转码过程中的时间基转换</h3><p>编解码器中的时间基(AVCodecContext.time_base,3.2节中的tbc)定义如下:</p><pre class="prism-token token language-javascript">typedef struct AVCodecContext <span class="token punctuation">{</span>
|
||
|
<span class="token operator">...</span><span class="token operator">...</span>
|
||
|
|
||
|
<span class="token comment">/**
|
||
|
* This is the fundamental unit of time (in seconds) in terms
|
||
|
* of which frame timestamps are represented. For fixed-fps content,
|
||
|
* timebase should be 1/framerate and timestamp increments should be
|
||
|
* identically 1.
|
||
|
* This often, but not always is the inverse of the frame rate or field rate
|
||
|
* for video. 1/time_base is not the average frame rate if the frame rate is not
|
||
|
* constant.
|
||
|
*
|
||
|
* Like containers, elementary streams also can store timestamps, 1/time_base
|
||
|
* is the unit in which these timestamps are specified.
|
||
|
* As example of such codec time base see ISO/IEC 14496-2:2001(E)
|
||
|
* vop_time_increment_resolution and fixed_vop_rate
|
||
|
* (fixed_vop_rate == 0 implies that it is different from the framerate)
|
||
|
*
|
||
|
* - encoding: MUST be set by user.
|
||
|
* - decoding: the use of this field for decoding is deprecated.
|
||
|
* Use framerate instead.
|
||
|
*/</span>
|
||
|
AVRational time_base<span class="token punctuation">;</span>
|
||
|
|
||
|
<span class="token operator">...</span><span class="token operator">...</span>
|
||
|
<span class="token punctuation">}</span></pre><p>上述注释指出,AVCodecContext.time_base是帧率(视频帧)的倒数,每帧时间戳递增1,那么tbc就等于帧率。编码过程中,应由用户设置好此参数。解码过程中,此参数已过时,建议直接使用帧率倒数用作时间基。
|
||
|
这里有一个问题:按照此处注释说明,帧率为25的视频流,tbc理应为25,但实际值却为50,不知作何解释?是否tbc已经过时,不具参考意义?</p><p>根据注释中的建议,实际使用时,在视频解码过程中,我们不使用AVCodecContext.time_base,而用帧率倒数作时间基,在视频编码过程中,我们将AVCodecContext.time_base设置为帧率的倒数。</p><h4 id="3.7.1-%E8%A7%86%E9%A2%91%E6%B5%81" name="3.7.1-%E8%A7%86%E9%A2%91%E6%B5%81">3.7.1 视频流</h4><p>视频按帧播放,所以解码后的原始视频帧时间基为 1/framerate。
|
||
|
视频解码过程中的时间基转换处理:</p><pre class="prism-token token language-javascript">AVFormatContext <span class="token operator">*</span>ifmt_ctx<span class="token punctuation">;</span>
|
||
|
AVStream <span class="token operator">*</span>in_stream<span class="token punctuation">;</span>
|
||
|
AVCodecContext <span class="token operator">*</span>dec_ctx<span class="token punctuation">;</span>
|
||
|
AVPacket packet<span class="token punctuation">;</span>
|
||
|
AVFrame <span class="token operator">*</span>frame<span class="token punctuation">;</span>
|
||
|
|
||
|
<span class="token comment">// 从输入文件中读取编码帧</span>
|
||
|
<span class="token function">av_read_frame</span><span class="token punctuation">(</span>ifmt_ctx<span class="token punctuation">,</span> <span class="token operator">&</span>packet<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
|
||
|
<span class="token comment">// 时间基转换</span>
|
||
|
int raw_video_time_base <span class="token operator">=</span> <span class="token function">av_inv_q</span><span class="token punctuation">(</span>dec_ctx<span class="token operator">-</span><span class="token operator">></span>framerate<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
<span class="token function">av_packet_rescale_ts</span><span class="token punctuation">(</span>packet<span class="token punctuation">,</span> in_stream<span class="token operator">-</span><span class="token operator">></span>time_base<span class="token punctuation">,</span> raw_video_time_base<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
|
||
|
<span class="token comment">// 解码</span>
|
||
|
<span class="token function">avcodec_send_packet</span><span class="token punctuation">(</span>dec_ctx<span class="token punctuation">,</span> packet<span class="token punctuation">)</span>
|
||
|
<span class="token function">avcodec_receive_frame</span><span class="token punctuation">(</span>dec_ctx<span class="token punctuation">,</span> frame<span class="token punctuation">)</span><span class="token punctuation">;</span></pre><p>视频编码过程中的时间基转换处理:</p><pre class="prism-token token language-javascript">AVFormatContext <span class="token operator">*</span>ofmt_ctx<span class="token punctuation">;</span>
|
||
|
AVStream <span class="token operator">*</span>out_stream<span class="token punctuation">;</span>
|
||
|
AVCodecContext <span class="token operator">*</span>dec_ctx<span class="token punctuation">;</span>
|
||
|
AVCodecContext <span class="token operator">*</span>enc_ctx<span class="token punctuation">;</span>
|
||
|
AVPacket packet<span class="token punctuation">;</span>
|
||
|
AVFrame <span class="token operator">*</span>frame<span class="token punctuation">;</span>
|
||
|
|
||
|
<span class="token comment">// 编码</span>
|
||
|
<span class="token function">avcodec_send_frame</span><span class="token punctuation">(</span>enc_ctx<span class="token punctuation">,</span> frame<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
<span class="token function">avcodec_receive_packet</span><span class="token punctuation">(</span>enc_ctx<span class="token punctuation">,</span> packet<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
|
||
|
<span class="token comment">// 时间基转换</span>
|
||
|
packet<span class="token punctuation">.</span>stream_index <span class="token operator">=</span> out_stream_idx<span class="token punctuation">;</span>
|
||
|
enc_ctx<span class="token operator">-</span><span class="token operator">></span>time_base <span class="token operator">=</span> <span class="token function">av_inv_q</span><span class="token punctuation">(</span>dec_ctx<span class="token operator">-</span><span class="token operator">></span>framerate<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
<span class="token function">av_packet_rescale_ts</span><span class="token punctuation">(</span><span class="token operator">&</span>opacket<span class="token punctuation">,</span> enc_ctx<span class="token operator">-</span><span class="token operator">></span>time_base<span class="token punctuation">,</span> out_stream<span class="token operator">-</span><span class="token operator">></span>time_base<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
|
||
|
<span class="token comment">// 将编码帧写入输出媒体文件</span>
|
||
|
<span class="token function">av_interleaved_write_frame</span><span class="token punctuation">(</span>o_fmt_ctx<span class="token punctuation">,</span> <span class="token operator">&</span>packet<span class="token punctuation">)</span><span class="token punctuation">;</span></pre><h4 id="3.7.2-%E9%9F%B3%E9%A2%91%E6%B5%81" name="3.7.2-%E9%9F%B3%E9%A2%91%E6%B5%81">3.7.2 音频流</h4><p>音频按采样点播放,所以解码后的原始音频帧时间基为 1/sample_rate
|
||
|
音频解码过程中的时间基转换处理:</p><pre class="prism-token token language-javascript">AVFormatContext <span class="token operator">*</span>ifmt_ctx<span class="token punctuation">;</span>
|
||
|
AVStream <span class="token operator">*</span>in_stream<span class="token punctuation">;</span>
|
||
|
AVCodecContext <span class="token operator">*</span>dec_ctx<span class="token punctuation">;</span>
|
||
|
AVPacket packet<span class="token punctuation">;</span>
|
||
|
AVFrame <span class="token operator">*</span>frame<span class="token punctuation">;</span>
|
||
|
|
||
|
<span class="token comment">// 从输入文件中读取编码帧</span>
|
||
|
<span class="token function">av_read_frame</span><span class="token punctuation">(</span>ifmt_ctx<span class="token punctuation">,</span> <span class="token operator">&</span>packet<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
|
||
|
<span class="token comment">// 时间基转换</span>
|
||
|
int raw_audio_time_base <span class="token operator">=</span> <span class="token function">av_inv_q</span><span class="token punctuation">(</span>dec_ctx<span class="token operator">-</span><span class="token operator">></span>sample_rate<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
<span class="token function">av_packet_rescale_ts</span><span class="token punctuation">(</span>packet<span class="token punctuation">,</span> in_stream<span class="token operator">-</span><span class="token operator">></span>time_base<span class="token punctuation">,</span> raw_audio_time_base<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
|
||
|
<span class="token comment">// 解码</span>
|
||
|
<span class="token function">avcodec_send_packet</span><span class="token punctuation">(</span>dec_ctx<span class="token punctuation">,</span> packet<span class="token punctuation">)</span>
|
||
|
<span class="token function">avcodec_receive_frame</span><span class="token punctuation">(</span>dec_ctx<span class="token punctuation">,</span> frame<span class="token punctuation">)</span><span class="token punctuation">;</span></pre><p>音频编码过程中的时间基转换处理:</p><pre class="prism-token token language-javascript">AVFormatContext <span class="token operator">*</span>ofmt_ctx<span class="token punctuation">;</span>
|
||
|
AVStream <span class="token operator">*</span>out_stream<span class="token punctuation">;</span>
|
||
|
AVCodecContext <span class="token operator">*</span>dec_ctx<span class="token punctuation">;</span>
|
||
|
AVCodecContext <span class="token operator">*</span>enc_ctx<span class="token punctuation">;</span>
|
||
|
AVPacket packet<span class="token punctuation">;</span>
|
||
|
AVFrame <span class="token operator">*</span>frame<span class="token punctuation">;</span>
|
||
|
|
||
|
<span class="token comment">// 编码</span>
|
||
|
<span class="token function">avcodec_send_frame</span><span class="token punctuation">(</span>enc_ctx<span class="token punctuation">,</span> frame<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
<span class="token function">avcodec_receive_packet</span><span class="token punctuation">(</span>enc_ctx<span class="token punctuation">,</span> packet<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
|
||
|
<span class="token comment">// 时间基转换</span>
|
||
|
packet<span class="token punctuation">.</span>stream_index <span class="token operator">=</span> out_stream_idx<span class="token punctuation">;</span>
|
||
|
enc_ctx<span class="token operator">-</span><span class="token operator">></span>time_base <span class="token operator">=</span> <span class="token function">av_inv_q</span><span class="token punctuation">(</span>dec_ctx<span class="token operator">-</span><span class="token operator">></span>sample_rate<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
<span class="token function">av_packet_rescale_ts</span><span class="token punctuation">(</span><span class="token operator">&</span>opacket<span class="token punctuation">,</span> enc_ctx<span class="token operator">-</span><span class="token operator">></span>time_base<span class="token punctuation">,</span> out_stream<span class="token operator">-</span><span class="token operator">></span>time_base<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
||
|
|
||
|
<span class="token comment">// 将编码帧写入输出媒体文件</span>
|
||
|
<span class="token function">av_interleaved_write_frame</span><span class="token punctuation">(</span>o_fmt_ctx<span class="token punctuation">,</span> <span class="token operator">&</span>packet<span class="token punctuation">)</span><span class="token punctuation">;</span></pre><h2 id="4.-%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99" name="4.-%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99">4. 参考资料</h2><p>[1]. <a data-from="10680" href="http://ffmpeg-users.933282.n4.nabble.com/What-does-the-output-of-ffmpeg-mean-tbr-tbn-tbc-etc-td941538.html" target="_blank" rel="nofollow noopener noreferrer">What does the output of ffmpeg mean? tbr tbn tbc etc?</a>
|
||
|
[2]. <a data-from="10680" href="https://www.cnblogs.com/leisure_chn/p/10285829.html" target="_blank" rel="nofollow noopener noreferrer">视频编解码基础概念</a>, <a data-from="10680" href="https://www.cnblogs.com/leisure_chn/p/10285829.html" target="_blank" rel="nofollow noopener noreferrer">https://www.cnblogs.com/leisure_chn/p/10285829.html</a>
|
||
|
[3]. <a data-from="10680" href="https://blog.csdn.net/topsluo/article/details/76239136" target="_blank" rel="nofollow noopener noreferrer">对ffmpeg的时间戳的理解笔记</a>, <a data-from="10680" href="https://blog.csdn.net/topsluo/article/details/76239136" target="_blank" rel="nofollow noopener noreferrer">https://blog.csdn.net/topsluo/article/details/76239136</a>
|
||
|
[4]. <a data-from="10680" href="http://www.imooc.com/article/91381" target="_blank" rel="nofollow noopener noreferrer">ffmpeg中的时间戳与时间基</a>, <a data-from="10680" href="http://www.imooc.com/article/91381" target="_blank" rel="nofollow noopener noreferrer">http://www.imooc.com/article/91381</a>
|
||
|
[5]. <a data-from="10680" href="http://www.52ffmpeg.com/article/353.html" target="_blank" rel="nofollow noopener noreferrer">ffmpeg编解码中涉及到的pts详解</a>, <a data-from="10680" href="http://www.52ffmpeg.com/article/353.html" target="_blank" rel="nofollow noopener noreferrer">http://www.52ffmpeg.com/article/353.html</a>
|
||
|
[6]. <a data-from="10680" href="https://blog.csdn.net/zhouyongku/article/details/38510747" target="_blank" rel="nofollow noopener noreferrer">音视频录入的pts和dts问题</a>, <a data-from="10680" href="https://blog.csdn.net/zhouyongku/article/details/38510747" target="_blank" rel="nofollow noopener noreferrer">https://blog.csdn.net/zhouyongku/article/details/38510747</a></p><h2 id="5.-%E4%BF%AE%E6%94%B9%E8%AE%B0%E5%BD%95" name="5.-%E4%BF%AE%E6%94%B9%E8%AE%B0%E5%BD%95">5. 修改记录</h2><p>2019-03-16 V1.0 初稿
|
||
|
2019-03-23 V1.1 增加3.7节</p><p>我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=1ea9bosv4dy7r</p></div><div class="col-article-source"><p>本文参与<a href="https://cloud.tencent.com/developer/support-plan" class="com-link">腾讯云自媒体分享计划</a>,欢迎正在阅读的你也加入,一起分享。</p></div><div class="col-article-time"><span>发表于 <time datetime="2019-04-02 15:40:26" title="2019-04-02 15:40:26"> 2019-04-02<span class="com-v-box">2019-04-02 15:40:26</span></time></span></div><div class="col-article-tags"><nav class="col-tags"><a href="https://cloud.tencent.com/developer/tag/10541?entry=article" class="col-tag">图像处理</a></nav><div class="extra-part"><div class="com-operations"><a href="javascript:;" class="com-opt-link link-warn"><i class="com-i-warn"></i>举报</a></div></div></div><div class="com-widget-operations" style="top: 114px;"><div class="main-cnt"><a href="javascript:;" class="com-opt-link link-like"><i class="com-i-like"></i><span class="text">4</span></a><a href="javascript:;" class="com-opt-link link-bookmark"><i class="com-i-bookmark"></i><span class="text">0</span></a></div><div class="extra-cnt"><span class="com-opt-text share-text">分享</span><ul class="com-share-options"><li><div class="c-bubble-trigger"><a href="javascript:;" class="opt-item"><i class="opt-icon wechat"></i></a><div class="c-bubble c-bubble-left "><div class="c-bubble-inner"><div class="qr-img" title="https://cloud.tencent.com/developer/article/1409507"><canvas width="100" height="100" style="display: none;"></canvas><img alt="Scan me!" style="display: block;" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAHLklEQVR4Xu2d0XLjOgxDm///6O6kTjI2QwGHstJJurxve+NKMkECIO2ml6+vr++vk/99fx+XuFwuTyvur7l/Hn/u+kP7n71/nl3v9hjdUrY+vX21Z/YZXXd/3TVyDQiM3K8CkmWrOifNtNlMjtVy/TetskPGJdV6/9xVKLkuVnE8J8F6H6NHhTQgW+hoAkUapQmaASQBcVw4mxH0wNXrXCIpvSLZuwdJ7aW0b7RPFsunCmlAnsNHQG1AbtrQFXKzuIpaKO1k4psJd1byWbmr6xx3x8+rBuKtK6QB2TqJGfpfqiHuAMpGEoF1Doj0CbQC3XlUM1yt3pe5rAZkg7EBuaXzn6wQV6r3z6lOKMtI18j2PEt/rr+gIj3bl42mCb/WqbsxhbOvlS66mlT0bKQCl41O6E0QC+osY1Y15P+5rFXZStYfVQ35WaqfLs5Lp73k4KtumgQ/E9jfSBYXdPV5AwKmyCTRllXINyFvCDnl2Kog03Vp1aj9q4FdGL6tmWxAjtn2doC4blhZUCL4zoVUZ2RU6LOzKSpypKCavzNW+KlCGpDn9wFookUqrPZZP5RFnqnPOBOXYfFzIpxZM0V7CFohKxiAAigfUKkANiDj6JyZWzUgIa50rEOS1ZkB6gCfKMuNp4mYKXpwop7R0gqbTII6uoYAR+2v05UGJDSGs1qzHBAnjvGgSldmnBrlYnUOVQVV0+AMhKMowgTZGnLaqzZtQLw9dlXTgIC5FaWsl1VIHJ2QjWZoY2bdCk1W+x5lHkb7qqnATDVkZgV16u5m3WF+OlDxjq1bPz04fC+LrO3ORiyriwGmf9Wpv8riUoF1gXpFJmedugr2jIGJCbZfQ45OGpAtdA3ILYX+ywq5izp9CJRxcswg141mtFDtxok2UOGm/L+iUtyZHqLegIwhVqOTM8kl+xCVcW7aqw5F1yUZTwVUreVocHZi4dalDLDkeUgDwm29pccVD6gakBcAUrW4tGut9hxnhFPtRczIiOpmqdKZm/S89wppQLyo769w1BOnE0qbDhpNbK8T3Ii0yyjlWlTlZQE5I6bUWRKhnzE+6boNyDHd1NxK9RDLAImiTrLBzZDcGvSmiX11e6m5kdKVKv+7yiYU90NzDci5CiGT4OsODUhIf2VanDYRQV5eIRjB5DkEGS1k9OAEmZypukaVipwVpu7UGZ1HLxdtb9VR7e0dCaASxpE2ES0ZXVMdfNLkmnWWzjo/DRdXuAVXvirAhB6ix68Kt0u6yPnUHpN13TUNyCBCWWI6rXHBJp83IO8GSFVDquJMdYXMoWjPoTLZUbIzCSOdozTtKm/616KpODcgeZ8z0sGl7/ZGcR05MDrLUq5LZbLLQuLmnE2lbizuZddVnfqsu6C0UKUgGmh6XZZAj37gxDerVm364bwNSD7WsJk8+aKeXZeIuutukZ3bZRytDKU/7saqVEHuIdPNM2dED6ici6IirQLSgGzRQYC4aiCZSa4ZZaTa31nL2bH+mXWpuGMToh7hzmZyA/KcbmVAFIc61xSdySyQ0ZtXB4PE3VAHVr2HGWZJ5eEVrwFVb2bUZDYghbkOncpSX59d918DUhVEJ4Sx6XLXzwJMq5FSsnOZoy6fUpY7r/yaWOqxMzAbkCN0dFyEAKHoV5sr19NEZ0LNxUiTKudzmTyqFLe3W7cBAbqpdDAyQQMS/tySmhB8VIUopGeFztnZFVToKCDq2wztnXGKitqyZhG9KNeAbBGgEwjclScjfvl76tQKK/GlYKq96HMZ5faqtpfa9HRAKEbzlhXUNzk0IBuMJFkoFTYgQMmrOpSBtAyQFb+wU6UDdb2iimrfst+HNK+jcylaqt67rZAGxL+Z/quAqC9Spg9fACscLqE3qCwrrTKSwcoVjX5eDT6zFoIaE/nN1g3IGM4GpPJLL/CroGZt8l7UadNYrhA6jST0RMU3cyZKiN312dnIM5UZyiJxmLlGvmyt0CfcfNa1kLGHu+mPBcTdGPmclmVV9LLEIM3aqKnL9o97uL6C6KurvOwe3ubPVZA5UXWc8ZGAkJccXHUQe0oC7vZxgKjMz9aetd9K5ypJkO7fgBwbw1nanUmWBiSUyVtXiLOqseTVTMaJGRX1eJ2dA8Gxt1pXnc0JfSVGQwdK3n5X/LvnzKpNpn1F9TGAm95+FCAuu2lwlI2kAqsAVhVd5fOZynNxcgZlJP5L/3x3NdD766uTggbkFr2ukC0QH1Eh9JCK45WuOM2h6yrxpYaDmCBKnQemIA+ozjRCGY3RwEWAG5AkmmSGRAPnMog8c3DiSUbsVVe2p6xqcrm9lor6mUaLaB
|
||
|
window.$render({"articleDetail":{"id":1409507,"articleId":1409507,"title":"FFmpeg时间戳详解","content":"entityMap|0|type|IMAGE|mutability|IMMUTABLE|data|imageUrl|https:\u002F\u002Fask.qcloudimg.com\u002Fhttp-save\u002Fyehe-4940323\u002Flv0b2oqwtf.jpeg|imageAlt|解码和显示顺序|1|LINK|MUTABLE|url|https:\u002F\u002Fgithub.com\u002Fleichn\u002Fblog_resources\u002Fblob\u002Fmaster\u002Fvideo\u002Ftuan\u002Ftnmil3.flv|2|http:\u002F\u002Fffmpeg-users.933282.n4.nabble.com\u002FWhat-does-the-output-of-ffmpeg-mean-tbr-tbn-tbc-etc-td941538.html|3|https:\u002F\u002Fwww.cnblogs.com\u002Fleisure_chn\u002Fp\u002F10285829.html|4|5|https:\u002F\u002Fblog.csdn.net\u002Ftopsluo\u002Farticle\u002Fdetails\u002F76239136|6|7|http:\u002F\u002Fwww.imooc.com\u002Farticle\u002F91381|8|9|http:\u002F\u002Fwww.52ffmpeg.com\u002Farticle\u002F353.html|10|11|https:\u002F\u002Fblog.csdn.net\u002Fzhouyongku\u002Farticle\u002Fdetails\u002F38510747|12|blocks|key|5q4v1|text|unstyled|depth|inlineStyleRanges|entityRanges|ei05r|1.+I帧\u002FP帧\u002FB帧|header-two|au1d0|I帧:I帧(Intra-coded+picture,+帧内编码帧,常称为关键帧)包含一幅完整的图像信息,属于帧内编码图像,不含运动矢量,在解码时不需要参考其他帧图像。因此在I帧图像处可以切换频道,而不会导致图像丢失或无法解码。I帧图像用于阻止误差的累积和扩散。在闭合式GOP中,每个GOP的第一个帧一定是I帧,且当前GOP的数据不会参考前后GOP的数据。|offset|length|style|BOLD|3cel0|P帧:P帧(Predictive-coded+picture,+预测编码图像帧)是帧间编码帧,利用之前的I帧或P帧进行预测编码。|5193h|B帧:B帧(Bi-directionally+predicted+picture,+双向预测编码图像帧)是帧间编码帧,利用之前和(或)之后的I帧或P帧进行双向预测编码。B帧不可以作为参考帧。\n+B帧具有更高的压缩率,但需要更多的缓冲时间以及更高的CPU占用率,因此B帧适合本地存储以及视频点播,而不适用对实时性要求较高的直播系统。|etbo3|2.+DTS和PTS|8mclu|DTS(Decoding+Time+Stamp,+解码时间戳),表示压缩帧的解码时间。\n+PTS(Presentation+Time+Stamp,+显示时间戳),表示将压缩帧解码后得到的原始帧的显示时间。\n+音频中DTS和PTS是相同的。视频中由于B帧需要双向预测,B帧依赖于其前和其后的帧,因此含B帧的视频解码顺序与显示顺序不同,即DTS与PTS不同。当然,不含B帧的视频,其DTS和PTS是相同的。下图以一个开放式GOP示意图为例,说明视频流的解码顺序和显示顺序\n+|fpsuf|?|atomic|9gmo1|\n+采集顺序指图像传感器采集原始信号得到图像帧的顺序。\n+编码顺序指编码器编码后图像帧的顺序。存储到磁盘的本地视频文件中图像帧的顺序与编码顺序相同。\n+传输顺序指编码后的流在网络中传输过程中图像帧的顺序。\n+解码顺序指解码器解码图像帧的顺序。\n+显示顺序指图像帧在显示器上显示的顺序。\n+采集顺序与显示顺序相同。编码顺序、传输顺序和解码顺序相同。\n+以图中“B[1]”帧为例进行说明,“B[1]”帧解码时需要参考“I[0]”帧和“P[3]”帧,因此“P[3]”帧必须比“B[1]”帧先解码。这就导致了解码顺序和显示顺序的不一致,后显示的帧需要先解码。|3hqhg|3.+FFmpeg中的时间基与时间戳|amh1c|3.1+时间基与时间戳的概念|header-three|2jp65|在FFmpeg中,时间基(time_base)是时间戳(timestamp)的单位,时间戳值乘以时间基,可以得到实际的时刻值(以秒等为单位)。例如,如果一个视频帧的dts是40,pts是160,其time_base是1\u002F1000,那么可以计算出此视频帧的解码时刻是40毫秒(40\u002F1000),显示时刻是160毫秒(160\u002F1000)。FFmpeg中时间戳(pts\u002Fdts)的类型是int64_t类型,把一个time_base看作一个时钟脉冲,则可把dts\u002Fpts看作时钟脉冲的计数。|66uj3|3.2+三种时间基tbr、tbn和tbc|7uslc|不同的封装格式具
|
||
|
</script><script src="./FFmpeg时间戳详解 - 云+社区 - 腾讯云_files/loader.js.下载" async=""></script><script src="./FFmpeg时间戳详解 - 云+社区 - 腾讯云_files/qcloudStatApi.js.下载" async=""></script><div class="c-nav com-nav-dropdown-wrap dropdown-p-search" style="position: fixed!important; z-index:1001;transform: none;-ms-transform: none;-moz-transform: none;-webkit-transform: none;-o-transform: none"> <div id="qcLeftNav" class="nav-dropdown-canvas" style="top: 10.4px; height: 739.6px;"> <div class="nav-dropdown-canvas-inner"> <div id="qcFstLeftNav" class="nav-dropdown-menu menu-level-1 J-qcLeftNavCol" style="display: none; height: 739.6px; left: -200px;" data-col="1"> <div class="nav-dropdown-menu-inner"> <div class="menu-area"> <ul> <li class="menu-link" data-id="compute" data-action="expand" data-link="https://cloud.tencent.com"> <a aria-haspopup="true" aria-expanded="false" role="button" aria-owns="qcSndLeftNav" href="javascript:;">腾讯云计算</a> </li> <li class="menu-link" data-id="product" data-action="expand" data-link="https://cloud.tencent.com/product"> <a aria-haspopup="true" aria-expanded="false" role="button" aria-owns="qcSndLeftNav" href="javascript:;">产品</a> </li> <li class="menu-link" data-id="solution" data-action="expand" data-link="https://cloud.tencent.com/solution/overview"> <a aria-haspopup="true" aria-expanded="false" role="button" aria-owns="qcSndLeftNav" href="javascript:;">解决方案</a> </li> <li class="menu-link" data-id="price" data-action="expand"> <a aria-haspopup="true" aria-expanded="false" role="button" aria-owns="qcSndLeftNav" href="javascript:;">定价</a> </li> <li class="menu-link" data-id="document" data-action="expand" data-link="https://cloud.tencent.com/document/product"> <a aria-haspopup="true" aria-expanded="false" role="button" aria-owns="qcSndLeftNav" href="javascript:;">文档</a> </li> <li class="menu-link" data-id="market" data-action="expand" data-link="https://market.cloud.tencent.com"> <a aria-haspopup="true" aria-expanded="false" role="button" aria-owns="qcSndLeftNav" href="javascript:;">云市场</a> </li> <li class="menu-link" data-id="developer" data-action="expand" data-link="https://cloud.tencent.com/developer"> <a aria-haspopup="true" aria-expanded="false" role="button" aria-owns="qcSndLeftNav" href="javascript:;">云+社区</a> </li> <li class="menu-link" data-id="college" data-action="expand" data-link="https://cloud.tencent.com/edu?ADTAG=gw.side"> <a aria-haspopup="true" aria-expanded="false" role="button" aria-owns="qcSndLeftNav" href="javascript:;">腾讯云大学</a> </li> <li class="menu-link" data-id="support" data-action="expand"> <a aria-haspopup="true" aria-expanded="false" role="button" aria-owns="qcSndLeftNav" href="javascript:;">支持</a> </li> <li class="menu-link" data-id="cooperate" data-action="expand"> <a aria-haspopup="true" aria-expanded="false" role="button" aria-owns="qcSndLeftNav" href="javascript:;">合作与生态</a> </li> </ul> </div> <div class="menu-area"> <ul> <li class="menu-link no-child"> <a aria-haspopup="false" role="button" href="https://cloud.tencent.com/act/free" target="_blank" hotrep="hp.left-nav.free">免费产品<i class="nav-icon-links"></i> </a> </li> <li class="menu-link no-child"> <a aria-haspopup="false" role="button" href="https://cloud.tencent.com/act/plan" target="_blank" hotrep="hp.left-nav.plan">云+创业<i class="nav-icon-links"></i> </a> </li> <li class="menu-link no-child"> <a role="button" href="https://cloud.tencent.com/product/events" target="_blank" hotrep="hp.left-nav.events"> 产品发布台<i class="nav-icon-links"></i> </a> </li> </ul> </div> <div class="menu-area"> <ul> <li class="menu-link no-child"> <a aria-haspopup="false" role="button" href="https://cloud.tencent.com/about/connect" hotrep="hp.left-nav.phone"> <i class="nav-icon-phone"></i> 联系我们 </a> </li> <li class="menu-link no-child"> <a aria-haspopup="false" role="button" href="https://cloud.tencent.com/developer/ask" hotrep="hp.left-nav.ask"> <i class="nav-icon-ask"></i> 快速问答 </a> </li>
|