日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ts打包代码详解 (ffmpeg)

發布時間:2023/12/29 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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部分代碼:

  • //nb_streams 對應es流,如果一路音頻一路視頻,nb_streams=2
  • ? ? for(i = 0; i < s->nb_streams; i++) {
  • ? ?? ???AVStream *st = s->streams[i];
  • ? ?? ???MpegTSWriteStream *ts_st = st->priv_data;
  • ? ?? ???switch(st->codec->codec_id) {
  • ? ?? ???case CODEC_ID_MPEG1VIDEO:
  • ? ?? ???case CODEC_ID_MPEG2VIDEO:
  • ? ?? ?? ?? ?stream_type = STREAM_TYPE_VIDEO_MPEG2;
  • ? ?? ?? ?? ?break;
  • ? ?? ???case CODEC_ID_MPEG4:
  • ? ?? ?? ?? ?stream_type = STREAM_TYPE_VIDEO_MPEG4;
  • ? ?? ?? ?? ?break;
  • ? ?? ???case CODEC_ID_H264:
  • ? ?? ?? ?? ?stream_type = STREAM_TYPE_VIDEO_H264;
  • ? ?? ?? ?? ?break;
  • ? ?? ???case CODEC_ID_MP2:
  • ? ?? ???case CODEC_ID_MP3:
  • ? ?? ?? ?? ?stream_type = STREAM_TYPE_AUDIO_MPEG1;
  • ? ?? ?? ?? ?break;
  • ? ?? ???case CODEC_ID_AAC:
  • ? ?? ?? ?? ?stream_type = STREAM_TYPE_AUDIO_AAC;
  • ? ?? ?? ?? ?break;
  • ? ?? ???case CODEC_ID_AC3:
  • ? ?? ?? ?? ?stream_type = STREAM_TYPE_AUDIO_AC3;
  • ? ?? ?? ?? ?break;
  • ? ?? ???default:
  • ? ?? ?? ?? ?stream_type = STREAM_TYPE_PRIVATE_DATA;
  • ? ?? ?? ?? ?break;
  • ? ?? ???}
  • ? ?? ???*q++ = stream_type;
  • ? ?? ???put16(&q, 0xe000 | ts_st->pid);
  • 復制代碼

    從上面看出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分別是數據和大小。

    PTS,DTS就是音視頻同步時間戳,時間戳其實就是一次采樣的顆粒(簡單理解就是數據),以視頻來舉例,視頻同步時鐘90K hz(27M/300),如果幀率是25fps的話,一幀數據采樣時間40ms,那么時間戳就是90K x 40ms = 3600(估算值)。
    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幀數據肯定一次包不完的。

    static?int?mpegts_write_packet_internal(AVFormatContext?*s,?AVPacket?*pkt)
    {
    ........
    ????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)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。