zoukankan      html  css  js  c++  java
  • FFMPEG中关于ts流的时长估计的实现(转)

       最近在做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
  • 相关阅读:
    一步一步学lucene——(第四步:搜索篇)
    如何搭建sshpermissions工程
    如何选择ESB
    一步一步学Mule ESB——(第二篇:Ajax篇)
    一步一步学lucene——(第二步:示例篇)
    一步一步学Mule ESB——(第一篇:基础篇)
    三步学会用spring开发OSGI——(第三步:web篇)
    关于SourceForge不能使用的问题
    一步一步学lucene——(第三步:索引篇)
    常用px,pt,em换算表
  • 原文地址:https://www.cnblogs.com/huaping-audio/p/3454781.html
Copyright © 2011-2022 走看看