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

来源:互联网 发布:淘宝上钱包店铺哪家好 编辑:程序博客网 时间:2024/05/16 16:05
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;}
0 0