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