如何用FFmpeg将输出的TSjava 将流写到文件中内存

&&&&&&&&&&&&&&&&&&
posts - 370,comments - 448,trackbacks - 1
&& 最近在做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 *
int read_size, i,
int64_t end_
int64_t filesize, offset,
int retry=0;
/* flush packet queue */
flush_packet_queue(ic);
for (i=0; i&ic-&nb_ 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;
offset = filesize - (DURATION_MAX_READ_SIZE&&retry);
if (offset & 0)
offset = 0;
avio_seek(ic-&pb, offset, SEEK_SET);//尽量往后查找,pts越靠近文件末尾,利用pts估计时长越准确
read_size = 0;
if (read_size &= DURATION_MAX_READ_SIZE&&(FFMAX(retry-1,0)))
ret = ff_read_packet(ic, pkt);//从接近文件末尾的地方读取数据,直到最后一个合法的数据包
} while(ret == AVERROR(EAGAIN));
if (ret != 0)
read_size += pkt-&
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数据得到比较接近的时长
if (st-&start_time != AV_NOPTS_VALUE)
duration -= st-&start_//减去初始时间
duration -= st-&first_
if (duration & 0) {
if (st-&duration == AV_NOPTS_VALUE || st-&duration & duration)
st-&duration =
av_free_packet(pkt);
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,
int bit_rate,
AVStream *
/* if bit_rate is already set, we believe it */
if (ic-&bit_rate &= 0) {
bit_rate = 0;
for(i=0;i&ic-&nb_i++) {//通过累积各个子流的平均码率得到文件的平均码率
st = ic-&streams[i];
if (st-&codec-&bit_rate & 0)
bit_rate += st-&codec-&bit_
ic-&bit_rate = bit_
/* 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_ 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 =
对应的,也可以在android stagefright中加入类似的实现:
uint64_t MPEG2TSExtractor::estimateDuration() {
Mutex::Autolock autoLock(mLock);
int64_t end_
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*
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) ) {
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
&& stream_id != 0xf1
&& stream_id != 0xff
// program_stream_directory
&& stream_id != 0xf2
&& 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_
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.
阅读(...) 评论()ffmpeg将h264和aac合成ts,内存输入输出
在内存中读取h264视频和aac音频,在内存中输出tsAVOutputFormat *ofmt = NULL;//Input AVFormatContext and Output AVFormatContextAVFormatContext *ifmt_ctx_v = NULL, *ifmt_ctx_a = NULL,*ofmt_ctx = NULL;AVPint ret,int videoindex_v=-1,videoindex_out=-1;int audioindex_a=-1,audioindex_out=-1;int frame_index=0;int64_t cur_pts_v=0,cur_pts_a=0;AVDictionary* pAVDictionary = NULL;AVInputFormat* ifmt = NULL;av_register_all();//Inputifmt_ctx_v = avformat_alloc_context();unsigned char * inH264=(unsigned char *)av_malloc(65535);
AVIOContext *vio =avio_alloc_context(inH264, 65535,0,this,fill_inH264,NULL,NULL);
ifmt_ctx_v-&pb=
int err = avformat_open_input(&ifmt_ctx_v, "nothing", NULL, NULL); if (err != 0){printf("\n");printf("Call avformat_open_input function failed!\n");}if ((ret = avformat_find_stream_info(ifmt_ctx_v, 0)) & 0) {printf( "Failed to retrieve input stream information");}ifmt_ctx_a = avformat_alloc_context();unsigned char * inAac=(unsigned char *)av_malloc(4096);
AVIOContext *aio =avio_alloc_context(inAac, 4096,0,this,fill_inAac,NULL,NULL);
ifmt_ctx_a-&pb=
err = avformat_open_input(&ifmt_ctx_a, "nothing", NULL, NULL); if (err != 0){printf("\n");printf("Call avformat_open_input function failed!\n");}if ((ret = avformat_find_stream_info(ifmt_ctx_a, 0)) & 0) {printf( "Failed to retrieve input stream information");}avformat_alloc_output_context2(&ofmt_ctx, NULL, "mpegts", NULL);
unsigned char* outbuffer=(unsigned char*)av_malloc(65535);
AVIOContext *avio_out =avio_alloc_context(outbuffer, 65535,0,this,NULL,write_buffer,NULL);
ofmt_ctx-&pb=avio_
ofmt_ctx-&flags=AVFMT_FLAG_CUSTOM_IO; //Output//avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);if (!ofmt_ctx) {printf( "Could not create output context\n");ret = AVERROR_UNKNOWN;}//注意循环次数for (i = 0; i & ifmt_ctx_v-&nb_ i++) {//Create output AVStream according to input AVStreamif(ifmt_ctx_v-&streams[i]-&codec-&codec_type==AVMEDIA_TYPE_VIDEO){AVStream *in_stream = ifmt_ctx_v-&streams[i];AVRrational.den = 1;rational.num = 15;in_stream-&r_frame_rate =AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream-&codec-&codec);videoindex_v=i;if (!out_stream) {printf( "Failed allocating output stream\n");ret = AVERROR_UNKNOWN;}videoindex_out=out_stream-&AVCodecContext*c = out_stream-&c-&bit_rate = 384000;c-&width = 1080;c-&height = 720;c-&time_base.num = 1;c-&time_base.den = 15; //Copy the settings of AVCodecContextif (avcodec_copy_context(out_stream-&codec, in_stream-&codec) & 0) {printf( "Failed to copy context from input to output stream codec context\n");}out_stream-&codec-&codec_tag = 0;if (ofmt_ctx-&oformat-&flags & AVFMT_GLOBALHEADER)out_stream-&codec-&flags |= CODEC_FLAG_GLOBAL_HEADER;}}for (i = 0; i & ifmt_ctx_a-&nb_ i++) {//Create output AVStream according to input AVStreamif(ifmt_ctx_a-&streams[i]-&codec-&codec_type==AVMEDIA_TYPE_AUDIO){AVStream *in_stream = ifmt_ctx_a-&streams[i];AVRrational.den = 1;rational.num = 15;in_stream-&r_frame_rate =AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream-&codec-&codec);audioindex_a=i;if (!out_stream) {printf( "Failed allocating output stream\n");ret = AVERROR_UNKNOWN;}audioindex_out=out_stream-&//Copy the settings of AVCodecContextif (avcodec_copy_context(out_stream-&codec, in_stream-&codec) & 0) {printf( "Failed to copy context from input to output stream codec context\n");}AVCodecContext*c = out_stream-&c-&sample_fmt = AV_SAMPLE_FMT_S16;
c-&bit_rate = 16000;
c-&sample_rate = 8000;
c-&channels = AV_CH_LAYOUT_MONO;
c-&time_base.num = 1;c-&time_base.den = 15; out_stream-&codec-&codec_tag = 0;if (ofmt_ctx-&oformat-&flags & AVFMT_GLOBALHEADER)out_stream-&codec-&flags |= CODEC_FLAG_GLOBAL_HEADER;}}//Write file headerif (avformat_write_header(ofmt_ctx, NULL) & 0) {printf( "Error occurred when opening output file\n");}while (1) {AVFormatContext *ifmt_int stream_index=0;AVStream *in_stream, *out_//Get an AVPacketif(av_compare_ts(cur_pts_v,ifmt_ctx_v-&streams[videoindex_v]-&time_base,cur_pts_a,ifmt_ctx_a-&streams[audioindex_a]-&time_base) &= 0){ifmt_ctx=ifmt_ctx_v;stream_index=videoindex_if(av_read_frame(ifmt_ctx, &pkt) &= 0){do{in_stream
= ifmt_ctx-&streams[pkt.stream_index];out_stream = ofmt_ctx-&streams[stream_index];if(pkt.stream_index==videoindex_v){//FIX:No PTS (Example: Raw H.264)//Simple Write PTSif(pkt.pts==AV_NOPTS_VALUE){//Write PTSAVRational time_base1=in_stream-&time_//Duration between 2 frames (us)int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(in_stream-&r_frame_rate);//Parameterspkt.pts=(double)(frame_index*calc_duration)/(double)(av_q2d(time_base1)*AV_TIME_BASE);pkt.dts=pkt.pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);frame_index++;}cur_pts_v=pkt.}}while(av_read_frame(ifmt_ctx, &pkt) &= 0);}else{}}else{ifmt_ctx=ifmt_ctx_a;stream_index=audioindex_if(av_read_frame(ifmt_ctx, &pkt) &= 0){do{in_stream
= ifmt_ctx-&streams[pkt.stream_index];out_stream = ofmt_ctx-&streams[stream_index];if(pkt.stream_index==audioindex_a){//FIX:No PTS//Simple Write PTSif(pkt.pts==AV_NOPTS_VALUE){//Write PTSAVRational time_base1=in_stream-&time_//Duration between 2 frames (us)int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(in_stream-&r_frame_rate);//Parameterspkt.pts=(double)(frame_index*calc_duration)/(double)(av_q2d(time_base1)*AV_TIME_BASE);pkt.dts=pkt.pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);frame_index++;}cur_pts_a=pkt.}}while(av_read_frame(ifmt_ctx, &pkt) &= 0);}else{}}//Convert PTS/DTSpkt.pts = av_rescale_q_rnd(pkt.pts, in_stream-&time_base, out_stream-&time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream-&time_base, out_stream-&time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));pkt.duration = av_rescale_q(pkt.duration, in_stream-&time_base, out_stream-&time_base);pkt.pos = -1;pkt.stream_index=stream_//printf("Write 1 Packet. size:%5d\tpts:%lld\n",pkt.size,pkt.pts);//Writeif (av_interleaved_write_frame(ofmt_ctx, &pkt) & 0) {printf( "Error muxing packet\n");}av_free_packet(&pkt);}//Write file trailerav_write_trailer(ofmt_ctx);
end:ifmt_ctx_v-&pb-&opaque = NULL;ifmt_ctx_a-&pb-&opaque = NULL;avio_close(ifmt_ctx_v-&pb);avio_close(ifmt_ctx_a-&pb);avformat_close_input(&ifmt_ctx_v);avformat_close_input(&ifmt_ctx_a);/* close output */ofmt_ctx-&pb-&opaque = NULL;if (ofmt_ctx && !(ofmt_ctx-&oformat-&flags & AVFMT_NOFILE))avio_close(ofmt_ctx-&pb);avformat_free_context(ofmt_ctx);if (ret & 0 && ret != AVERROR_EOF) {printf( "Error occurred.\n");}
没有更多推荐了,
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!使用ffmpeg封装ts流的问题
[问题点数:36分,结帖人sghcpt]
本版专家分:0
结帖率 100%
CSDN今日推荐
本版专家分:0
本版专家分:0
结帖率 100%
本版专家分:0
2012年7月 专题开发/技术/项目大版内专家分月排行榜第一2012年5月 专题开发/技术/项目大版内专家分月排行榜第一
2012年4月 专题开发/技术/项目大版内专家分月排行榜第二2012年3月 专题开发/技术/项目大版内专家分月排行榜第二
本版专家分:0
结帖率 100%
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:0
匿名用户不能发表回复!|
CSDN今日推荐MPEG的系统层编码为不同的应用场景设计了两种格式:&
TS(Transport Stream)&和PS(Program Stream),
它们两者之间不具有层级关系,
在逻辑上,它们两者都是由PES(Packetized Elementary Stream)包组成的,
所以可以很方便地实现相互转换.
TS(Transport Stream):&
& 是将具有一个或多个独立时间基的一个或多个节目(包括音频和视频)组成一个流,
& 组成同一个节目的基本流(如一个视频流,多个音频流)的PES包有一个共用的时间基。
& TS的包长标准为188bytes.
从上面的定义可以分成三层来看TS/PS。
ES层 & : 由单独的音频(如mp3),视频流(如h.264)组成基本的ES(Elementary Stream)。
PES层 &: 将基本的ES按一定的规则(如H.264以AU)进行封装,并打上时间戳,组成PES。
TS/PS层: 将PES包进行切分后再封装成188bytes大小的TS包,
& & & & &同时还将一些节目信息也封装成TS包(称为section), 两者共同组成TS层。
从上面的总结,TS/PS总体上来说,是一种封装格式,用来承载数据。
所以FFmpeg
将TS/PS的解析文件定义在libavformat/mpegts.c文件中
将音频,视频的解码定义在libavcodec/mpeg12.c文件中
下面来看FFmpeg是如何进行TS的demuxer的。
1. MPEG2-TS的demuxer函数
AVInputFormat ff_mpegts_demuxer&=&{&
&&&&"mpegts",&
&&&&NULL_IF_CONFIG_SMALL("MPEG-2 transport stream format"),
&&&&sizeof(MpegTSContext),
&&&&mpegts_probe,
&&&&mpegts_read_header,
&&&&mpegts_read_packet,&
&&&&mpegts_read_close,&
&&&&read_seek,
&&&&mpegts_get_pcr,
&&&&.flags&=&AVFMT_SHOW_IDS|AVFMT_TS_DISCONT,&
#ifdef USE_SYNCPOINT_SEARCH&
&&&&.read_seek2&=&read_seek2,&
2. 解析流中的TS格式
&* 出现3种格式,主要原因是:
&* TS标准是 188B
&* 日本标准是192Bytes的DVH-S格式;
&* 第三种的 204Bytes则是在188Bytes的基础上,加上16Bytes的FEC(前向纠错).
#define TS_PACKET_SIZE 188
#define TS_DVHS_PACKET_SIZE 192
#define TS_FEC_PACKET_SIZE 204
#define TS_MAX_PACKET_SIZE 204
//&&maximum score,&half of that&is&used&for&file-extension-based detection
#define AVPROBE_SCORE_MAX 100
&* 函数功能:
&* 分析流中是三种TS格式的哪一种
static&int&mpegts_probe(AVProbeData&*p)
#define CHECK_COUNT 10
&&const&int&size=&p-&buf_size;
&&int&score,&fec_score,&dvhs_score;
&&int&check_count=&size&/&TS_FEC_PACKET_SIZE;
&&if&(check_count&&&CHECK_COUNT)
&&&&&&return&-1;
&&score & &&=&analyze(p-&buf,&TS_PACKET_SIZE&*check_count,&TS_PACKET_SIZE&,&NULL)&
& & & & & & & *&CHECK_COUNT&/&check_count;
&&dvhs_score=&analyze(p-&buf,&TS_DVHS_PACKET_SIZE*check_count,&TS_DVHS_PACKET_SIZE,&NULL)
& & & & & & & *&CHECK_COUNT&/&check_count;
&&fec_score&=&analyze(p-&buf,&TS_FEC_PACKET_SIZE&*check_count,&TS_FEC_PACKET_SIZE&,&NULL)
& & & & & & & *&CHECK_COUNT&/&check_count;
&&&*&we need a clear definition&for&the returned score&,
&&&*&otherwise things will become messy sooner&or&later
&&if&(score&&&fec_score&&&&score&&&dvhs_score&&&&score&&&6)&
&&&&return AVPROBE_SCORE_MAX&+&score&-&CHECK_COUNT;
&&else&if(dvhs_score&&&score&&&&dvhs_score&&&fec_score&&&&dvhs_score&&&6)&
&&&&return AVPROBE_SCORE_MAX&+&dvhs_score&-&CHECK_COUNT;
&&else&if(fec_score&&&6)&
&&&&return AVPROBE_SCORE_MAX&+&fec_score&-&CHECK_COUNT;
&&&&return&-1;
&* 函数功能:
&* 在size大小的buf中,寻找满足特定格式,长度为packet_size的
&* packet的个数;
&* 显然,返回的值越大越可能是相应的格式(188/192/204)
static&int&analyze(const&uint8_t&*buf,&int&size,&int&packet_size,&int&*index){
&&int&stat[TS_MAX_PACKET_SIZE];
&&int&x=0;
&&int&best_score=0;
&&memset(stat,&0,&packet_size*sizeof(int));
&&for&(x=i=0;&i&&&size-3;&i++)
&&&&if&((buf[i]&==&0x47)&&&&!(buf[i+1]&&&0x80)&&&&(buf[i+3]&&&0x30))
&&&&&&stat[x]++;
&&&&&&&&&&&&
&&&&&&if&(stat[x]&&&best_score)
&&&&&&&&best_score=&stat[x];
&&&&&&&&if&(index)&
&&&&&&&&&&*index=&x;
&&&&if&(x&==&packet_size)&
&&&&&&x=&0;&
&&return best_score;
buf[i] == 0x47 &
& &其中的sync_byte固定为0x47,即上面的.&
!(buf[i+1] & 0x80) &&
& &由于transport_error_indicator为1的TS Packet实际有错误,
& &表示携带的数据无意义, 这样的Packet显然没什么意义.
buf[i+3] & 0x30&
& &对于adaptation_field_control, 如果取值为0x00,则表示为未来保留,现在不用.
这就是MPEG TS的侦测过程.
3. MPEG2-TS头解析
#define NB_PID_MAX 8192
#define MAX_SECTION_SIZE 4096
/*&pids&*/
#define PAT_PID 0x0000
#define SDT_PID 0x0011
/*&table ids&*/
#define PAT_TID 0x00
#define PMT_TID 0x02
#define SDT_TID 0x42
&*&函数功能:
int&mpegts_read_header(AVFormatContext&*s,&AVFormatParameters&*ap)
&&&* MpegTSContext , 是为了解码不同容器格式所使用的私有数据,
&&&* 只有在相应的诸如mpegts.c文件才可以使用的.
&&&* 这样,增加了这个库的模块化.
&&MpegTSContext&*ts&=&s-&priv_data;
&&AVIOContext&*pb&=&s-&pb;
&&uint8_t buf[8*1024];
&&int&len;
&&int64_t pos;
&&/*&read the first 8*1024 bytes&to&get&packet size&*/
&&pos&=&avio_tell(pb);& & & & & & & & & &// 获取buf的当前位置
&&len&=&avio_read(pb,&buf,&sizeof(buf));&// 从pb-&opaque中读取sizeof(buf)个字节到buf
&&if&(len&!=&sizeof(buf))
&&&&goto fail;
&&&* 获得TS包的实际长度
&&ts-&raw_packet_size&=&get_packet_size(buf,&sizeof(buf));
&&if&(ts-&raw_packet_size&&=&0)&
&&&&av_log(s,&AV_LOG_WARNING,&"Could not detect TS packet size, defaulting to non-FEC/DVHS\n");
&&&&ts-&raw_packet_size&=&TS_PACKET_SIZE;
&&ts-&stream&=&s;&
&&ts-&auto_guess&=&0;
&&if&(s-&iformat&==&&ff_mpegts_demuxer)&
&&&&/*&normal demux&*/
&&&&/*&first&do&a scaning&to&get&all the services&*/
&&&&if&(avio_seek(pb,&pos,&SEEK_SET)&&&0)
&&&&&&av_log(s,&AV_LOG_ERROR,&"Unable to seek back to the start\n");
&&&&&* 挂载了两个Section类型的过滤器,
&&&&&* 其实在TS的两种负载中,section是PES的元数据,
&&&&&* 只有先解析了section,才能进一步解析PES数据,因此先挂上section的过滤器。
&&&&mpegts_open_section_filter(ts,&SDT_PID,&sdt_cb,&ts,&1);
&&&&mpegts_open_section_filter(ts,&PAT_PID,&pat_cb,&ts,&1);
&&&&handle_packets(ts,&s-&probesize&/&ts-&raw_packet_size);
&&&&/*&if&could&not&find service,&enable auto_guess&*/
&&&&ts-&auto_guess&=&1;
&&&&av_dlog(ts-&stream,&"tuning done\n");
&&&&s-&ctx_flags&|=&AVFMTCTX_NOHEADER;
&&avio_seek(pb,&pos,&SEEK_SET);&
&&return 0;
&&return&-1;
MpegTSFilter&*mpegts_open_section_filter(MpegTSContext*&ts,&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&unsigned&int&pid,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&SectionCallback*&section_cb,&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&void*&opaque,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&int&check_crc)
&&MpegTSFilter&*filter;
&&MpegTSSectionFilter&*sec;&
&&av_dlog(ts-&stream,&"Filter: pid=0x%x\n",&pid);
&&if&(pid&&=&NB_PID_MAX&||&ts-&pids[pid])
&&&&return&NULL;
&&filter&=&av_mallocz(sizeof(MpegTSFilter));
&&if&(!filter)
&&&&return&NULL;
&&ts-&pids[pid]&=&filter;
&&filter-&type&=&MPEGTS_SECTION;
&&filter-&pid&=&pid;&
&&filter-&last_cc&=&-1;
&&sec&=&&filter-&u.section_filter;
&&sec-&section_cb&=&section_cb;
&&sec-&opaque&=&opaque;
&&sec-&section_buf=&av_malloc(MAX_SECTION_SIZE);
&&sec-&check_crc&=&check_crc;
&&if&(!sec-&section_buf)&
&&&&av_free(filter);
&&&&return&NULL;
&&return&filter;
对于这部分代码,需要分析数据结构的定义:
& & struct MpegTSC
& & & & & & & &|
& & & & & & & &V
& & struct MpegTSF
& & & & & & & &|
& & & & & & & &V
+--------------+---------------+
| & & & & & & & & & & & & & & &|
V & & & & & & & & & & & & & & &V
MpegTSPESFilter & & & &MpegTSSectionFilter
就是struct MpegTSContext;中有NB_PID_MAX(8192)个TS的Filter,
而每个struct MpegTSFilter
& 可能是 PES & &的Filter
& 或者是 Section的Filter。
为什么NB_PID_MAX 是 8192,
需要看TS的语法结构(ISO/IEC
Syntax & & & & & & & & & & & & &No.&of bits & & & & Mnemonic
transport_packet(){&
&&sync_byte & & & & & & & & & & & &8 & & & & & & & & bslbf
&&transport_error_indicator & & & &1 & & & & & & & & bslbf
&&payload_unit_start_indicator & & 1 & & & & & & & & bslbf
&&transport_priority & & & & & & & 1 & & & & & & & & bslbf
&&PID & & & & & & & & & & & & & & &13 & & & & & & & &uimsbf
&&transport_scrambling_control & & 2 & & & & & & & & bslbf
&&adaptation_field_control & & & & 2 & & & & & & & & bslbf
&&continuity_counter & & & & & & & 4 & & & & & & & & uimsbf
&&if&(adaptation_field_control=='10'&||&
&&&&&&adaptation_field_control=='11'&)
&&&&&&&&adaptation_field()&
&&if&(adaptation_field_control=='01'&||&
&&&&&&adaptation_field_control=='11'&)&
&&&&for&(i=0;i&N;i++)
&&&&&&data_byte & & & & & & & & & & 8 & & & & & & & &bslbf
而8192,是2^13=8192(PID)的最大数目,
为什么会有PES和Section的区分,更详细的可以参考ISO/IEC-13818-1.
挂载上了两种section过滤器,如下:
=========================================================================
PID & & & & & & & &|Section Name & & & & & |Callback
=========================================================================
SDT_PID(0x0011) & &|ServiceDescriptionTable|sdt_cb
& & & & & & & & & &| & & & & & & & & & & & |
PAT_PID(0x0000) & &|ProgramAssociationTable|pat_cb
=========================================================================
设计成回调函数,是为了在后面使用。
4. MPEG2-TS的包处理
int&handle_packets(MpegTSContext&*ts,&int&nb_packets)
&&AVFormatContext&*s&=&ts-&stream;
&&uint8_t packet[TS_PACKET_SIZE];
&&int&packet_num,&ret;
&&ts-&stop_parse&=&0;
&&packet_num&=&0;
&&for&(&;&;&)&
&&&&packet_num++;
&&&&if&(nb_packets&!=&0&&&&packet_num&&=&nb_packets&||
&&&&&&&&ts-&stop_parse&&&1)&
&&&&&&ret&=&AVERROR(EAGAIN);
&&&&&&break;
&&&&if&(ts-&stop_parse&&&0)
&&&&&&break;
&&&&ret&=&read_packet(s,&packet,&ts-&raw_packet_size);
&&&&if&(ret&!=&0)
&&&&&&return ret;
&&&&ret&=&handle_packet(ts,&packet);
&&&&if&(ret&!=&0)
&&&&&&return ret;
&&return 0;&
它的代码结构很简单:
handle_packets()
& & +-&read_packet()
& & +-&handle_packet()
& & & & +-&write_section_data()
read_packet(), &很简单, 就是去找sync_byte(0x47),
handle_packet(),是真正处理数据的地方.它的代码如下:
&* 功能: handle one TS packet&
int&handle_packet(MpegTSContext&*ts,&const&uint8_t&*packet)
&&AVFormatContext&*s&=&ts-&stream;
&&MpegTSFilter&*tss;
&&int&len,&pid,&cc,&expected_cc,&cc_ok,&afc,&is_start;
&&const&uint8_t&*p,&*p_end;
&&int64_t pos;
&&/* 获取该包的PID */
&&pid&=&AV_RB16(packet&+&1)&&&0x1fff;
&&if&(pid&&&&discard_pid(ts,&pid))
&&&&&return 0;
&&&* 是否是PES或者Section的开头
&&&* 即syntax element: payload_unit_start_indicator&
&&is_start&=&packet[1]&&&0x40;
&&tss&=&ts-&pids[pid];
&&&* ts-&auto_guess此时为0,因此不考虑下面的代码
&&if&(ts-&auto_guess&&&&tss&==&NULL&&&&is_start)&
&&&&add_pes_stream(ts,&pid,&-1);
&&&&tss&=&ts-&pids[pid];
&&if&(!tss)
&&&&return 0;
&&&* continuity check (currently not used)&
&&&* 虽然检查,但不利用检查的结果
&&cc&=&(packet[3]&&&0xf);
&&expected_cc&=&(packet[3]&&&0x10)&?&(tss-&last_cc&+&1)&&&0x0f&:&tss-&last_cc;
&&cc_ok&=&(tss-&last_cc&&&0)&||&(expected_cc&==&cc);
&&tss-&last_cc&=&cc;
&&&* 解析 adaptation_field_control 语法元素
&&&* =======================================================
&&&* 00 | Reserved for future use by ISO/IEC
&&&* 01 | No adaptation_field, payload only
&&&* 10 | Adaptation_field only, no payload
&&&* 11 | Adaptation_field follwed by payload
&&&* =======================================================
&&afc&=&(packet[3]&&&&4)&&&3;
&&p&=&packet&+&4;
&&if&(afc&==&0)&/*&reserved value&*/
&&&&return 0;&
&&if&(afc&==&2)&/*&adaptation field only&*/&
&&&&return 0;
&&if&(afc&==&3)&
&&&&&* 跳过 adapation field&
&&&&&* p[0]对应的语法元素为: adaptation_field_length
&&&&p&+=&p[0]&+&1;
&&&* if past the end of packet, ignore&
&&&* p已近到达TS包中的有效负载的地方
&&p_end&=&packet&+&TS_PACKET_SIZE;
&&if&(p&&=&p_end)
&&&&return 0;
&&pos&=&avio_tell(ts-&stream-&pb);
&&ts-&pos47=&pos&%&ts-&raw_packet_size;
&&if&(tss-&type&==&MPEGTS_SECTION)&
&&&&&* 针对Section, 第一个字节对应的语法元素为:pointer_field(见2.4.4.1),
&&&&&* 它表示在当前TS包中,从pointer_field开始到第一个section的第一个字节间的字节数。
&&&&&* 当TS包中有至少一个section的起始时,
&&&&&*& & payload_unit_start_indicator = 1 且 TS负载的第一个字节为pointer_
&&&&&*& & pointer_field = 0x00时,表示section的起始就在这个字节之后;
&&&&&* 当TS包中没有section的起始时,&
&&&&&*& & payload_unit_start_indicator = 0 且 TS负载中没有pointer_
&&&&if&(is_start)&
&&&&&&/*&pointer field present&*/
&&&&&&len&=&*p++;
&&&&&&if&(p&+&len&&&p_end)
&&&&&&&&return 0;
&&&&&&if&(len&&&&cc_ok)&
&&&&&&&&/*&
&&&&&&&&&* write remaining section bytes&
&&&&&&&&&* TS包的负载部分由Section A的End部分和Section B的Start组成,
&&&&&&&&&* 先把Section A的End部分写入
&&&&&&&&&*/
&&&&&&&&write_section_data(s,&tss,&p,&len,&0);
&&&&&&&&/*&check whether&filter&has been closed&*/
&&&&&&&&if&(!ts-&pids[pid])
&&&&&&&&&&return 0;
&&&&&&p&+=&len;
&&&&&&if&(p&&&p_end)&
&&&&&&&&/*
&&&&&&&&&* 再将Section B的Start部分写入
&&&&&&&&&*/
&&&&&&&&write_section_data(s,&tss,&p,&p_end&-&p,&1);
&&&&&&/* TS包负载仅是一个Section的中间部分部分,将其写入*/
&&&&&&if&(cc_ok)&
&&&&&&&&write_section_data(s,&tss,&p,&p_end&-&p,&0);
&&&&int&ret;
&&&&&* 如果是PES类型,直接调用其Callback,
&&&&&* 但显然,只有Section部分解析完成后才可能解析PES
&&&&//&Note:&The position here points actually behind the current packet.
&&&&if&((ret&=&tss-&u.pes_filter.pes_cb(tss,&p,&p_end&-&p,&is_start,
&&&&&&&&pos&-&ts-&raw_packet_size))&&&0)
&&&&&&return ret;
&&return 0;
write_section_data()函数:
& 反复收集buffer中的数据,指导完成相关Section的重组过程,
& 然后调用之前注册的两个section_cb.
5. 节目指定信息的解析
&* PAT(Program Association Table) 节目相关表
&* 提供了节目号与PID值的对应关系
&* 见ISO/IEC .4.4.3 Table 2-30
void pat_cb(MpegTSFilter&*filter,&const&uint8_t&*section,&int&section_len);
&* PMT(Program Map Table) 节目映射表
&* 提供了节目号与组成节目的元素之间的映射关系--或者称为"节目定义"
&* 见ISO/IEC .4.4.8 Table 2-33
void pmt_cb(MpegTSFilter&*filter,&const&uint8_t&*section,&int&section_len);
&* SDT(Transport Stream Description Table) TS描述表
&* 用于定义TS描述子的表
&* 见ISO/IEC .4.4.12 Table 2-36
void sdt_cb(MpegTSFilter&*filter,&const&uint8_t&*section,&int&section_len)
6. 解析PES包
&* 见ISO/IEC .4.3.6 Table 2-21
int&mpegts_push_data(MpegTSFilter*&filter,
&&&&&&&&&&&&&&&&&&&&&const&uint8_t*&buf,&
&&&&&&&&&&&&&&&&&&&&&int&buf_size,&
&&&&&&&&&&&&&&&&&&&&&int&is_start,
&&&&&&&&&&&&&&&&&&&&&int64_t pos);
至此,整个TS层的解析基本完成。
阅读(...) 评论()

我要回帖

更多关于 java 将流写到文件中 的文章

 

随机推荐