ts流中的时间估计
我们知道ts流中是没有时间信息的,我门来看看ffmpeg是怎么估计其duration的
方法1.通过pts来估计
static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset)
{
AVPacket pkt1, *pkt = &pkt1;
AVStream *st;
int read_size, i, ret;
int64_t end_time;
int64_t filesize, offset, duration;
int retry=0; /* flush packet queue */
flush_packet_queue(ic); for (i=0; i<ic->nb_streams; i++) {
st = ic->streams[i];
if (st->start_time == AV_NOPTS_VALUE && st->first_dts == AV_NOPTS_VALUE)
av_log(st->codec, AV_LOG_WARNING, "start time is not set in estimate_timings_from_pts\n"); if (st->parser) {
av_parser_close(st->parser);
st->parser= NULL;
}
} /* estimate the end time (duration) */
/* XXX: may need to support wrapping */
filesize = ic->pb ? avio_size(ic->pb) : 0;//得到文件大小
end_time = AV_NOPTS_VALUE;
do{
offset = filesize - (DURATION_MAX_READ_SIZE<<retry);
if (offset < 0)
offset = 0; avio_seek(ic->pb, offset, SEEK_SET);//尽量往后查找,pts越靠近文件末尾,利用pts估计时长越准确
read_size = 0;
for(;;) {
if (read_size >= DURATION_MAX_READ_SIZE<<(FFMAX(retry-1,0)))
break; do {
ret = ff_read_packet(ic, pkt);//从接近文件末尾的地方读取数据,直到最后一个合法的数据包
} while(ret == AVERROR(EAGAIN));
if (ret != 0)
break;
read_size += pkt->size;
st = ic->streams[pkt->stream_index];
if (pkt->pts != AV_NOPTS_VALUE &&
(st->start_time != AV_NOPTS_VALUE ||
st->first_dts != AV_NOPTS_VALUE)) {
duration = end_time = pkt->pts;//利用该包的pts数据得到比较接近的时长
if (st->start_time != AV_NOPTS_VALUE)
duration -= st->start_time;//减去初始时间
else
duration -= st->first_dts;
if (duration > 0) {
if (st->duration == AV_NOPTS_VALUE || st->duration < duration)
st->duration = duration;
}
}
av_free_packet(pkt);
}
}while( end_time==AV_NOPTS_VALUE
&& filesize > (DURATION_MAX_READ_SIZE<<retry)
&& ++retry <= DURATION_MAX_RETRY);//尝试 DURATION_MAX_RETRY这么多次
} 方法2:通过文件大小和码流来估计
static void estimate_timings_from_bit_rate(AVFormatContext *ic)
{
int64_t filesize, duration;
int bit_rate, i;
AVStream *st; /* if bit_rate is already set, we believe it */
if (ic->bit_rate <= 0) {
bit_rate = 0;
for(i=0;i<ic->nb_streams;i++) {//通过累积各个子流的平均码率得到文件的平均码率
st = ic->streams[i];
if (st->codec->bit_rate > 0)
bit_rate += st->codec->bit_rate;
}
ic->bit_rate = bit_rate;
} /* if duration is already set, we believe it */
if (ic->duration == AV_NOPTS_VALUE &&
ic->bit_rate != 0) {
filesize = ic->pb ? avio_size(ic->pb) : 0;
if (filesize > 0) {
for(i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
duration= av_rescale(8*filesize, st->time_base.den, ic->bit_rate*(int64_t)st->time_base.num);//通过文件大小除以文件平均码率得到文件时长,之所以还有time_base信息,是因为最后要把秒转换为以time_base为单位的值
if (st->duration == AV_NOPTS_VALUE)
st->duration = duration;
}
}
}
}
对应的,也可以在android stagefright中加入类似的实现:
uint64_t MPEG2TSExtractor::estimateDuration() {
Mutex::Autolock autoLock(mLock);
int64_t filesize;
int64_t end_time;
int64_t offset, duration;
int retry=1;
status_t re=mDataSource->getSize(&filesize);//android中有类似的函数去得到文件的大小
if (re != OK) {
ALOGE("Failed to get file size");
return ERROR_MALFORMED;
}
uint8_t packet[kTSPacketSize];
unsigned payload_unit_start_indicator = 0;
unsigned PID = 0;
unsigned adaptation_field_control = 0;
//实现思想:从文件末尾开始读取188个字节,直到找到第一个pes包的边界,并且跳过adp filed的包,这里认为adp field不含有合法的pts
while (1) {
offset = filesize - kTSPacketSize*retry;
ssize_t n = mDataSource->readAt(offset, packet, kTSPacketSize);
if (n < (ssize_t)kTSPacketSize) {
return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM;
}
ABitReader* br= new ABitReader((const uint8_t *)packet, kTSPacketSize);//主要此类实现了一个bit读取器,对于码流的解析非常方便,类似于ffmpeg中的get_bits.h中实现的功能
unsigned sync_byte = br->getBits(8);
CHECK_EQ(sync_byte, 0x47u);
br->skipBits(1);
unsigned payload_unit_start_indicator = br->getBits(1);
br->skipBits(1);
PID = br->getBits(13);
br->skipBits(2);
adaptation_field_control = br->getBits(2);
if ((payload_unit_start_indicator == 1) && (adaptation_field_control == 1) &&
(PID != 0x00u) && (PID != 0x01u) && (PID != 0x02u) ) {
break;
}
retry++;
delete br;
} ;
ABitReader* br= new ABitReader((const uint8_t *)packet, kTSPacketSize);
br->skipBits(8 + 3 + 13);
br->skipBits(2);
adaptation_field_control = br->getBits(2);
ALOGV("adaptation_field_control = %u", adaptation_field_control);
br->skipBits(4);
if (adaptation_field_control == 2 || adaptation_field_control == 3) {
unsigned adaptation_field_length = br->getBits(8);
if (adaptation_field_length > 0) {
br->skipBits(adaptation_field_length * 8);
}
ALOGV("adaptation_field_length = %u", adaptation_field_length);
}
if (adaptation_field_control == 1 || adaptation_field_control == 3) {
unsigned packet_startcode_prefix = br->getBits(24); ALOGV("packet_startcode_prefix = 0x%08x", packet_startcode_prefix);
CHECK_EQ(packet_startcode_prefix, 0x000001u); unsigned stream_id = br->getBits(8);
ALOGV("stream_id = 0x%02x", stream_id);
br->skipBits(16);
//以下可以参考标准,标准上解释的很详细,应该不难理解
if (stream_id != 0xbc // program_stream_map
&& stream_id != 0xbe // padding_stream
&& stream_id != 0xbf // private_stream_2
&& stream_id != 0xf0 // ECM
&& stream_id != 0xf1 // EMM
&& stream_id != 0xff // program_stream_directory
&& stream_id != 0xf2 // DSMCC
&& stream_id != 0xf8) { // H.222.1 type E
CHECK_EQ(br->getBits(2), 2u);
br->skipBits(6);
unsigned PTS_DTS_flags = br->getBits(2);
ALOGV("PTS_DTS_flags = %u", PTS_DTS_flags);
br->skipBits(6); unsigned PES_header_data_length = br->getBits(8); unsigned optional_bytes_remaining = PES_header_data_length; uint64_t PTS = 0, DTS = 0; if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) {
CHECK_GE(optional_bytes_remaining, 5u); CHECK_EQ(br->getBits(4), PTS_DTS_flags); PTS = ((uint64_t)br->getBits(3)) << 30;
CHECK_EQ(br->getBits(1), 1u);
PTS |= ((uint64_t)br->getBits(15)) << 15;
CHECK_EQ(br->getBits(1), 1u);
PTS |= br->getBits(15);
CHECK_EQ(br->getBits(1), 1u); ALOGV("PTS = %llu", PTS);
ALOGV("PTS = %.2f secs", PTS / 90000.0f); PTS = (PTS * 1000 * 1000ll) / 90000;
return PTS;
}
// ES data follows.
}
}
delete br;
return 0;
}

FFMPEG中关于ts流的时长估计的实现的更多相关文章

  1. FFMPEG中关于ts流的时长估计的实现(转)

    最近在做H.265 编码,原本只是做编码器的实现,但客户项目涉及到ts的封装,搞得我不得不配合了解点ts方面的东西.下面技术文档不错,转一下. ts流中的时间估计 我们知道ts流中是没有时间信息的,我 ...

  2. (原)关于MEPG-2中的TS流数据格式学习

    关于MEPG-2中的TS流数据格式学习 Author:lihaiping1603 原创:http://www.cnblogs.com/lihaiping/p/8572997.html 本文主要记录了, ...

  3. RobotFramework 截取中文中的数字比较时长

    先看下需求,这个报表中有个时长,需要对昨日和前日的时长进行比较,我们获取到的元素是例如“9分43秒”这样的格式 1.首先要讲中文中的分和秒分别提取出来 提取python代码如下: import res ...

  4. 使用FFMPEG进行一些视频处理(C#)视频合并、转码、获取时长

    FFMPEG的强大无需多说,举几个用到的功能,直接贴代码了 还有更多命令用到时搜索即可 视频转码 ) { var args = "-y -i {0} -vcodec copy {1}&quo ...

  5. ffmpeg 中av_rescale_rnd 的含义

    http://blog.csdn.net/fireroll/article/details/8485482 一.函数声明: int64_t av_rescale_rnd(int64_t a, int6 ...

  6. ffmpeg解析TS流

    介绍:  MPEG的系统层编码为不同的应用场景设计了两种格式:  TS(Transport Stream) 和PS(Program Stream), 它们两者之间不具有层级关系, 在逻辑上,它们两者都 ...

  7. 分析ffmpeg解析ts流信息的源码

    花费一些时间,然后全部扔了.为了不忘记和抛砖引玉,特发此贴. ffmpeg解析ts流 1.目的     打算软件方式解析出pat,pmt等码流信息 2.源代码所在位置         下载ffmpeg ...

  8. 获取音、视频时长(NAudio,Shell32,FFmpeg)

    参考网址:https://blog.csdn.net/u013810234/article/details/57471780 以下为本次测试用到的音.视频格式: audio :”.wav;.mp3;. ...

  9. spa(单页应用)中,使用history模式时,微信长按识别二维码在ios下失效的问题

    spa(单页应用,vue)中,使用history模式时,微信长按识别二维码在ios下失效的问题. 触发条件: spa单页应用: 路由模式 history 从其他页面跳转到带有微信二维码识别的页面(不是 ...

随机推荐

  1. sql server博客

    SQLSERVER MSDN论坛 SQLSERVER 补丁博客 SQLSERVER中国研发中心 微软亚太区数据库技术支持组官方博客 PAUL的SQLSKILL网站 sqlsaturday网站 sqls ...

  2. 64位下安装Scrapy 报错 "could not find openssl.exe" 的解决方法。

    其实就是安装对应的64位 pyOpenSSL 就行了, 下载地址如下: https://tahoe-lafs.org/source/tahoe-lafs/deps/tahoe-lafs-dep-egg ...

  3. Shell实现多级菜单系统安装维护脚本实例分享

    Shell实现多级菜单系统安装维护脚本实例分享 这篇文章主要介绍了Shell实现多级菜单系统安装维护脚本实例分享,本文脚本用多级菜单实现管理WEB服务器.Mysql服务器.Nginx服器等,需要的朋友 ...

  4. projecteuler----&gt;problem=11----Largest product in a grid

    In the 2020 grid below, four numbers along a diagonal line have been marked in red. 08 02 22 97 38 1 ...

  5. Java solr 索引数据增删改查

    具体代码如下: import java.io.IOException; import java.util.*; import org.apache.solr.client.solrj.SolrClie ...

  6. Java线程:概念与原理(转)

    一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程.比如在 ...

  7. 【Swift】学习笔记(三)——字符和字符串

    基本了解了变量的创建和基础数据类型,但是在开发中用得最多的还是字符串.那什么是字符串呢? Swift 的String类型表示特定序列的Character(字符) 类型值的集合,它是值类型具有可变性 S ...

  8. Android开发之JNI(一)--HelloWorld及遇到的错误解析

    Android开发之JNI(一)--HelloWorld及遇到的错误解析 1.NDK环境搭建    參考http://blog.csdn.net/xiaoliouc/article/details/8 ...

  9. JavaScript操作dom总结

    最近一直忙于新项目,真的挺费心的 从产品原型.ui.接口.真心挺费心的.好多地方都不完善!(i want say F word!!) 基础的东西又需要重新看看了! Node Node.NodeType ...

  10. 倍福TwinCAT(贝福Beckhoff)常见问题(FAQ)-报错0X4650,18000错误怎么办

    驱动器未就绪错误,请重启驱动器 注意,即便是报这个错,仍然有可能上电成功,这样的话可以直接在TwinCAT中清除错误即可     更多教学视频和资料下载,欢迎关注以下信息: 我的优酷空间: http: ...