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

摘要:
ts流中的时间估计。我们知道ts流中没有时间信息。让我们来看看ffmpeg是如何根据端口{AVPacketpkt1,*pkt=&pkt1;AVStream*st;intrad_size,i,ret;int64_tend_time;int64_tfilesize,offset,duration;intertry=0;/*flushpacketqueue*/flushpacket-queue;对于{st=ic-˃stream[i];ifav_log;如果{av_parser_close;st-˃parser=NULL;}/*估计结束时间*//*XXX:可能需要支持包装*/filesize=ic-˃pb?avio_size:0;//获取文件大小end_ time=AV_ NOPTS_ VALUE;做{offset=filesize-;ifoffset=0;avio_seek;//尝试向后看。pts越接近文件末尾,pts估计时间长度read_size=0;for(;)越准确{ifbreak;do{ret=ff_read_packet;//读取文件末尾附近的数据,直到最后一个合法数据包}while;如果(ret!=AV_NOPTS_VALUE)){duration=end_time=pkt-˃pts;//如果(st-˃start_time!=AV_NOPTS_VALVE)duration-=st-˃start_time;//减去初始时间elseduration-=st-˃first_dts;如果{ifst-˃duration=duration;}av_ free_数据包;}}同时;//多次尝试DURATION_ MAX_ RETRY}方法2:通过文件大小和代码流_ timings_ from_bit_ Rate{int64_tfilesize,DURATION;intbit_Rate,i;AVStream*st;/*ifbit_rateisalreadyset,webbelieeit*/if{bit_Rate=0;for{//累加每个子流的平均比特率,以获得文件st=ic-˃流的平均码率[i]; ifbit_rate+=st-˃codec-˃bit_rate;}ic-˃比特率=比特率;}/*如果持续时间已设置,webbelieeit*/if(ic-˃duration==AV_NOPTS_VALUE&&ic-˃bit_rate!

   最近在做H.265 编码,原本只是做编码器的实现,但客户项目涉及到ts的封装,搞得我不得不配合了解点ts方面的东西。下面技术文档不错,转一下。

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
");

        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;
}

linking: http://www.mmihome.net/portal.php?mod=view&aid=14

免责声明:文章转载自《FFMPEG中关于ts流的时长估计的实现(转)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Linux下安装mysql-5.7.30详细步骤c# 图片保存为流Stream,再次加载时提示System.ArgumentOutOfRangeException: 要求非负数的解决办法下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

php pack、unpack、ord 函数使用方法

string pack ( string $format [, mixed $args [, mixed $... ]] ) Pack given arguments into a binary string according to format. The idea for this function was taken from Perl and a...

FFmpeg时间戳详解

本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10584910.html 1. I帧/P帧/B帧 I帧:I帧(Intra-coded picture, 帧内编码帧,常称为关键帧)包含一幅完整的图像信息,属于帧内编码图像,不含运动矢量,在解码时不需要参考其他帧图像。因此在I帧图像处可以切换频道,而...

码片速率的含义

1.符号速率 符号速率*扩频因子=码片速率,符号速率=码片速率/扩频因子 如: WCDMA, 码片速率= 3.84 MHz ,扩频因子=4 ,则符号速率=960kbps. CDMA 1X, 码片速率=1.2288MHz,扩频因子=64,则符号速率=19.2kbps. 符号速率=(业务速率+校验码)*信道编码*打孔率 如: WCDMA ,业务速率=384kb...

C语言的面向对象设计 —— 对 X264/FFMPEG 架构探讨

类似题目的文章已经不新鲜了,这里,我仅仅总结自己的一些代码经验,结合两款在视频开发领域比较常用的开源软件探讨C语言的应用问题。 1.为什么要用C语言 曾几何时,我也不熟悉C,最早接触C的是在大学四年级,当时已经学过pascal,过二级也是pascal。接着走上了Delphi的路,多方便的软件,写写画画,程序就出来了,本科的毕业设计就是这样出来的MIS,在当...

【设计经验】3、ISE中烧录QSPI Flash以及配置mcs文件的加载速度与传输位宽

一、软件与硬件平台        软件平台:               操作系统:Windows 7 64-bit               开发套件:ISE14.7        硬件平台:               FPGA型号:XC6SLX45-CSG324               QSPI Flash型号:W25Q128BV 二、背景介绍...

雪花算法(snowflake)

简单描述 最高位是符号位,始终为0,不可用。 41位的时间序列,精确到毫秒级,41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序。注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截) 后得到的值,这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序...