ts打包代码详解 (ffmpeg)
FFmpeg代碼里面有ts打包和解包的代碼,這里簡單介紹下怎么使用吧。
先來看下FFmpeg目錄結構:
libavformat:用于各種音視頻封裝格式的生成和解析,包括獲取解碼所需信息以生成解碼上下文結構和讀取音視頻幀等功能;
libavcodec:用于各種類型聲音/圖像編解碼;
libavutil:包含一些公共的工具函數;
libswscale:用于視頻場景比例縮放、色彩映射轉換;
libpostproc:用于后期效果處理;
ffmpeg:該項目提供的一個工具,可用于格式轉換、解碼或電視卡即時編碼等;
ffsever:一個HTTP 多媒體即時廣播串流服務器;
ffplay:是一個簡單的播放器,使用ffmpeg庫解析和解碼,通過SDL顯示;
libavformat目錄下 mpegtsenc.c,mpegts.c 分別是ts打包和解包的代碼:
下面介紹下mpegtsenc.c一些重要函數(原理請看 iso 13818-1):
1) PSI業務數據
????? mpegts_write_pat(AVFormatContext *s);
? ?? ?mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
? ?? ?mpegts_write_sdt(AVFormatContext *s)//節目描述表
? ???pat,pmt這兩個表是ts打包最重要的表,這兩個表說白了就是多路復用的一個索引,解碼器需要更具PAT知道有哪些節目(可以理解為電視節目),根據PMT知道每個節目里面有哪些es流(每個電視節目都有音頻和視頻),這兩個函數一般是不需要改動的;
? ??pat,pmt的信息并不是只是開始打包的時候出現,看mpegts_write_pes代碼會發現著兩個表是根據retransmit_si_info計算出來的。
? ? 看下mpegts_write_pmt部分代碼:
復制代碼
從上面看出ts打包支持MPEG1,MPEG2,MPEG4,h264視頻以及PCM,mp2,mp3,AAC,AC3音頻,音頻方面標準不支持G711等G開頭的音頻,當然如果自己開發客戶端的話是可以自定義的。
2)mpegts_write_header(AVFormatContext *s)
初始化AVFormatContext參數,在正式封裝開始加入PAT,PMT,SDT一些信息。代碼中有基本的注釋;
3)mpegts_write_pes(AVFormatContext *s, AVStream *st, const uint8_t *payload, int payload_size, int64_t pts, int64_t dts)
這個函數就是TS打包的主函數了,這個函數主要功能就是把一幀數據拆分成188字節(感覺效率低了點),并加入PTS,DTS同步信息,這個函數封裝的對象是一幀視頻或者音頻數據,payload,payload_size分別是數據和大小。
static?void?mpegts_write_pes(AVFormatContext?*s,?AVStream?*st,
?????????????????????????????const?uint8_t?*payload,?int?payload_size,
???????????????????????????? int64_t?pts,?int64_t?dts,?int?key)
{????
........
??? while?(payload_size?>?0)?{
????????retransmit_si_info(s,?force_pat);???//評估是否需要插入PAT、PMT
????????force_pat?=?0;
????????write_pcr?=?0;
????????if?(ts_st->pid?==?ts_st->service->pcr_pid)?{????//評估pes中是否需要插入PCR
????????????if?(ts->mux_rate?>?1?||?is_start)?//?VBR?pcr?period?is?based?on?frames
????????????????ts_st->service->pcr_packet_count++;
????????????if?(ts_st->service->pcr_packet_count?>=
????????????????ts_st->service->pcr_packet_period)?{
????????????????ts_st->service->pcr_packet_count?=?0;
????????????????write_pcr?=?1;
????????????}
????????}
????????if?(ts->mux_rate?>?1?&&?dts?!=?AV_NOPTS_VALUE?&&
????????????(dts?-?get_pcr(ts,?s->pb)/300)?>?delay)?{???//CBR考慮(插入pcr_only或null_packet)
????????????/*?pcr?insert?gets?priority?over?null?packet?insert?*/
????????????if?(write_pcr)
????????????????mpegts_insert_pcr_only(s,?st);
????????????else
????????????????mpegts_insert_null_packet(s);
????????????continue;?/*?recalculate?write_pcr?and?possibly?retransmit?si_info?*/
????????}
????????/*?prepare?packet?header?*/
????????q?=?buf;
????????*q++?=?0x47;
????????val?=?(ts_st->pid?>>?8);
????????if?(is_start)
????????????val?|=?0x40;????//payload_unit_start_indicator
????????*q++?=?val;
????????*q++?=?ts_st->pid;
????????ts_st->cc?=?(ts_st->cc?+?1)?&?0xf;??//continuity_counter
????????*q++?=?0x10?|?ts_st->cc;?//?payload?indicator?+?CC
????????/*********寫入adaptation_field(如果需要)?*********/
????????if?(key?&&?is_start?&&?pts?!=?AV_NOPTS_VALUE)?{
????????????if?(ts_st->pid?==?ts_st->service->pcr_pid)
????????????????write_pcr?=?1;
????????????set_af_flag(buf,?0x40);?//set?Random?Access?for?key?frames
????????????q?=?get_ts_payload_start(buf);
????????}
????????if?(write_pcr)?{
????????????set_af_flag(buf,?0x10);?//PCR_flag
????????????q?=?get_ts_payload_start(buf);
????????????//?add?11,?pcr?references?the?last?byte?of?program?clock?reference?base
????????????if?(ts->mux_rate?>?1)
????????????????pcr?=?get_pcr(ts,?s->pb);
????????????else
????????????????pcr?=?(dts?-?delay)*300;
????????????if?(dts?!=?AV_NOPTS_VALUE?&&?dts?<?pcr?/?300)
????????????????av_log(s,?AV_LOG_WARNING,?"dts?<?pcr,?TS?is?invalid\n");
????????????extend_af(buf,?write_pcr_bits(q,?pcr));?//寫入pcr
????????????q?=?get_ts_payload_start(buf);
????????}
????????/*********寫入payload*********/
????????if?(is_start)?{
????????????int?pes_extension?=?0;
????????????int?pes_header_stuffing_bytes?=?0;
????????????/*?write?PES?header?*/
????????????*q++?=?0x00;
????????????*q++?=?0x00;
????????????*q++?=?0x01;
????????????is_dvb_subtitle?=?0;
????????????is_dvb_teletext?=?0;
????????????/*?寫入stream_id?*/
????????????if?(st->codec->codec_type?==?AVMEDIA_TYPE_VIDEO)?{
????????????????if?(st->codec->codec_id?==?AV_CODEC_ID_DIRAC)?{
????????????????????*q++?=?0xfd;
????????????????}?else
????????????????????*q++?=?0xe0;????//video?stream
????????????}?else?if?(st->codec->codec_type?==?AVMEDIA_TYPE_AUDIO?&&
???????????????????????(st->codec->codec_id?==?AV_CODEC_ID_MP2?||
????????????????????????st->codec->codec_id?==?AV_CODEC_ID_MP3?||
????????????????????????st->codec->codec_id?==?AV_CODEC_ID_AAC))?{
????????????????*q++?=?0xc0;
????????????}?else?if?(st->codec->codec_type?==?AVMEDIA_TYPE_AUDIO?&&
????????????????????????st->codec->codec_id?==?AV_CODEC_ID_AC3?&&
????????????????????????ts->m2ts_mode)?{
????????????????*q++?=?0xfd;
????????????}?else?{
????????????????*q++?=?0xbd;
????????????????if(st->codec->codec_type?==?AVMEDIA_TYPE_SUBTITLE)?{
????????????????????if?(st->codec->codec_id?==?AV_CODEC_ID_DVB_SUBTITLE)?{
????????????????????????is_dvb_subtitle?=?1;
????????????????????}?else?if?(st->codec->codec_id?==?AV_CODEC_ID_DVB_TELETEXT)?{
????????????????????????is_dvb_teletext?=?1;
????????????????????}
????????????????}
????????????}
????????????header_len?=?0;
????????????flags?=?0;
????????????/*?處理PTS_DTS_flags?*/
????????????if?(pts?!=?AV_NOPTS_VALUE)?{
????????????????header_len?+=?5;
????????????????flags?|=?0x80;
????????????}
????????????if?(dts?!=?AV_NOPTS_VALUE?&&?pts?!=?AV_NOPTS_VALUE?&&?dts?!=?pts)?{
????????????????header_len?+=?5;
????????????????flags?|=?0x40;
????????????}
........
????????????len?=?payload_size?+?header_len?+?3;
????????????/*?3?extra?bytes?should?be?added?to?DVB?subtitle?payload:?0x20?0x00?at?the?beginning?and?trailing?0xff?*/
????????????if?(is_dvb_subtitle)?{
????????????????len?+=?3;
????????????????payload_size++;
????????????}
????????????if?(len?>?0xffff)
????????????????len?=?0;
????????????if?(ts->omit_video_pes_length?&&?st->codec->codec_type?==?AVMEDIA_TYPE_VIDEO)?{
????????????????len?=?0;
????????????}
????????????*q++?=?len?>>?8;????//PES_packet_length
????????????*q++?=?len;
????????????val?=?0x80;?//'10'
????????????/*?data?alignment?indicator?is?required?for?subtitle?and?data?streams?*/
????????????if?(st->codec->codec_type?==?AVMEDIA_TYPE_SUBTITLE?||?st->codec->codec_type?==?AVMEDIA_TYPE_DATA)
????????????????val?|=?0x04;
????????????*q++?=?val;
????????????*q++?=?flags;
????????????*q++?=?header_len;??//PES_header_data_length
????????????if?(pts?!=?AV_NOPTS_VALUE)?{
????????????????write_pts(q,?flags?>>?6,?pts);??//寫入pts
????????????????q?+=?5;
????????????}
????????????if?(dts?!=?AV_NOPTS_VALUE?&&?pts?!=?AV_NOPTS_VALUE?&&?dts?!=?pts)?{
????????????????write_pts(q,?1,?dts);???//寫入dts
????????????????q?+=?5;
????????????}
........
????????/*?處理完header,下面開始處理數據?*/
????????/*?header?size?*/
????????header_len?=?q?-?buf;
????????/*?data?len?*/
????????len?=?TS_PACKET_SIZE?-?header_len;
????????if?(len?>?payload_size)?//pes包太大,一個ts包發不完,就得拆包。
????????????len?=?payload_size;
????????stuffing_len?=?TS_PACKET_SIZE?-?header_len?-?len;???//相反,如果太小,就加入填充。
????????if?(stuffing_len?>?0)?{
????????????/*?add?stuffing?with?AFC?*/?????//
????????????if?(buf[3]?&?0x20)?{
????????????????/*?stuffing?already?present:?increase?its?size?*/
????????????????afc_len?=?buf[4]?+?1;
????????????????memmove(buf?+?4?+?afc_len?+?stuffing_len,
????????????????????????buf?+?4?+?afc_len,
????????????????????????header_len?-?(4?+?afc_len));
????????????????buf[4]?+=?stuffing_len;
????????????????memset(buf?+?4?+?afc_len,?0xff,?stuffing_len);
????????????}?else?{
????????????????/*?add?stuffing?*/
????????????????memmove(buf?+?4?+?stuffing_len,?buf?+?4,?header_len?-?4);
????????????????buf[3]?|=?0x20;
????????????????buf[4]?=?stuffing_len?-?1;
????????????????if?(stuffing_len?>=?2)?{
????????????????????buf[5]?=?0x00;
????????????????????memset(buf?+?6,?0xff,?stuffing_len?-?2);
????????????????}
????????????}
????????}
????????if?(is_dvb_subtitle?&&?payload_size?==?len)?{
????????????memcpy(buf?+?TS_PACKET_SIZE?-?len,?payload,?len?-?1);
????????????buf[TS_PACKET_SIZE?-?1]?=?0xff;?/*?end_of_PES_data_field_marker:?an?8-bit?field?with?fixed?contents?0xff?for?DVB?subtitle?*/
????????}?else?{
????????????memcpy(buf?+?TS_PACKET_SIZE?-?len,?payload,?len);???//寫入payload數據
????????}
????????payload?+=?len;
????????payload_size?-=?len;
????????mpegts_prefix_m2ts_header(s);
????????avio_write(s->pb,?buf,?TS_PACKET_SIZE);?//寫入avio(緩存到avio_buf中)
????}
????avio_flush(s->pb);??//把avio_buf的數據寫入后端(file/udp等)
????ts_st->prev_payload_key?=?key;
}
4)mpegts_write_packet(AVFormatContext *s,??AVPacket *pkt)
這個函數功能比較簡單,就是把一幀數據拆分成幾個塊來封裝成pes,因為pes頭信息的長度只有兩個字節長度(當時可能面向標清),高清的I幀數據肯定一次包不完的。
{
........
????if?(st->codec->codec_id?==?AV_CODEC_ID_H264)?{
????????const?uint8_t?*p?=?buf,?*buf_end?=?p+size;
????????uint32_t?state?=?-1;
????????int?ret?=?ff_check_h264_startcode(s,?st,?pkt);
????????if?(ret?<?0)
????????????return?ret;
????????do?{
????????????p?=?avpriv_find_start_code(p,?buf_end,?&state);
????????????av_dlog(s,?"nal?%d\n",?state?&?0x1f);
????????}?while?(p?<?buf_end?&&?(state?&?0x1f)?!=?9?&&
?????????????????(state?&?0x1f)?!=?5?&&?(state?&?0x1f)?!=?1);???//nal_unit_type
????????if?((state?&?0x1f)?!=?9)?{?//?AUD?NAL
????????????data?=?av_malloc(pkt->size+6);
????????????if?(!data)
????????????????return?AVERROR(ENOMEM);
????????????memcpy(data+6,?pkt->data,?pkt->size);
????????????AV_WB32(data,?0x00000001);
????????????data[4]?=?0x09;
????????????data[5]?=?0xf0;?//?any?slice?type?(primary_pic_type?=?0xe)?+?rbsp?stop?one?bit
????????????buf??=?data;
????????????size?=?pkt->size+6;
????????}
????if?(pkt->dts?!=?AV_NOPTS_VALUE)?{
????????int?i;
????????for(i=0;?i<s->nb_streams;?i++){
????????????AVStream?*st2?=?s->streams[i];
????????????MpegTSWriteStream?*ts_st2?=?st2->priv_data;
????????????if(???ts_st2->payload_size
???????????????&&?(ts_st2->payload_dts?==?AV_NOPTS_VALUE?||?dts?-?ts_st2->payload_dts?>?delay/2)){
????????????????mpegts_write_pes(s,?st2,?ts_st2->payload,?ts_st2->payload_size,
????????????????????????????????ts_st2->payload_pts,?ts_st2->payload_dts,
????????????????????????????????ts_st2->payload_flags?&?AV_PKT_FLAG_KEY);???//優先處理其它碼流fifo數據(多碼流同步考慮)
????????????????ts_st2->payload_size?=?0;
????????????}
????????}
????}
????if?(ts_st->payload_size?&&?ts_st->payload_size?+?size?>?ts->pes_payload_size)?{
????????mpegts_write_pes(s,?st,?ts_st->payload,?ts_st->payload_size,
?????????????????????????ts_st->payload_pts,?ts_st->payload_dts,
?????????????????????????ts_st->payload_flags?&?AV_PKT_FLAG_KEY);???//處理當前碼流fifo數據
????????ts_st->payload_size?=?0;
????}
????if?(st->codec->codec_type?!=?AVMEDIA_TYPE_AUDIO?||?size?>?ts->pes_payload_size)?{
????????av_assert0(!ts_st->payload_size);
????????//?for?video?and?subtitle,?write?a?single?pes?packet
????????mpegts_write_pes(s,?st,?buf,?size,?pts,?dts,?pkt->flags?&?AV_PKT_FLAG_KEY);?//滿一個pes幀,直接寫入。
????????av_free(data);
????????return?0;
????}
????if?(!ts_st->payload_size)?{?//初始化pts、dts
????????ts_st->payload_pts?=?pts;
????????ts_st->payload_dts?=?dts;
????????ts_st->payload_flags?=?pkt->flags;
????}
????memcpy(ts_st->payload?+?ts_st->payload_size,?buf,?size);????//不滿一個pes幀,先緩存到fifo。
????ts_st->payload_size?+=?size;
????av_free(data);
總結
以上是生活随笔為你收集整理的ts打包代码详解 (ffmpeg)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中国非处方药(OTC)市场营销策略及十四
- 下一篇: 二值图像分析:二值图像轮廓提取