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

   

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

2015-10-27  gljin_cn

   最近在做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\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;
}

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

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。如发现有害或侵权内容,请点击这里 或 拨打24小时举报电话:4000070609 与我们联系。

    0条评论

    发表

    请遵守用户 评论公约

    类似文章
    喜欢该文的人也喜欢 更多

    ×
    ×

    ¥2.00

    微信或支付宝扫码支付:

    《个图VIP服务协议》

    全部>>