ts demuxer的加入记录

系统 1639 0

文件夹

1 初衷

2 ts demux的功能介绍


1 初衷


    之前打算给dtplayer加入一些亮点功能,最初的想法是:bt下载播放 + hls支持

    bt下载因为以来libtorrent库,尽管搞懂了怎样加入,但须要改动libtorrent库来集成,

    若将libtorrent集成到代码中,会将代码变得庞大,框架清晰度会变差,随机临时取消了bt功能的开发/

   

    后面開始加入hls支持,hls支持打算加入例如以下模块: stream - hls   demuxer - ts  decoder - h264 这样加上之前的faad

    对于特定网络流就能够不依赖ffmpeg来播放了。

    ts demuxer便是第一步功能。

    凭借之前对ts的理解,感觉应该比較快完毕的,当时从github中找到了一个开源的ts解析库: https://github.com/nevali/tsdemux

    但不幸的是这个库仅仅提供了 section的解析功能,并没有提供读取es包的功能。遂打算自行将这部分功能补全。


代码:  https://github.com/peterfuture/dtplayer/blob/dev-ts/dtdemux/demuxer/demuxer_ts.c



2 ts demux的功能

    ts demux符合dtplayer demuxer的标准接口,

    先看下定义:

      

demuxer_wrapper_t demuxer_ts = {

     . name = "ts demuxer" ,

     . id = DEMUXER_TS ,

     . probe = ts_probe ,

     . open = ts_open ,

     . read_frame = ts_read_frame ,

     . setup_info = ts_setup_info ,

     . seek_frame = ts_seek_frame ,

     . close = ts_close

};

以下依次介绍各个功能。


2.1 probe

      

static int ts_probe ( demuxer_wrapper_t * wrapper , dt_buffer_t * probe_buf )

{

     const uint8_t * buf = probe_buf -> data ;

     const uint8_t * end = buf + probe_buf -> level - 7 ;

    

     if ( probe_buf -> level < 10 )

         return 0 ;

     int retry_times = 100 ;

     for (; buf < end ; buf ++ )

     {

         uint32_t header = DT_RB8 ( buf );

         if (( header & 0xFF ) != 0x47 )

         {

             if ( retry_times -- == 0 )

                 return 0 ;

             continue ;

         }

         //found 0x47

         if ( buf + 188 > end )

             return 0 ;

         header = DT_RB8 ( buf + 188 );

         if (( header & 0xFF ) == 0x47 )

         {

             dt_info ( TAG , "ts detect \n " );

             return 1 ;

         }

         else

             return 0 ;

     }

     return 0 ;

}

这里实现了一个比較简单的probe功能,先找到同步字0x47,若后面188字节后面也是0x47,则觉得是ts流,临时probe有些流还不能解析

主要问题有: a 有些流的包大小并非188, 这样的流会出错 b 单纯的仅仅推断一次会有偶然性的问题

但这些修正起来比較简单,因为功能还未实现,这里仅仅介绍思想,还有面会完好

2 ts_open

这里是解析ts头信息,主要功能包含:

pat解析

pmt解析

stream 信息解析: duration - bitrate

es信息解析:audio: channel-samplerate-bps  video: width-height-fmt

这里代码比較多就仅仅说下思想,感兴趣的自己去读代码就能够了

首先pat pmt的解析是通过之前说的开源库完毕的,通过解析pat pmt能够得到文件里有几个流,各自是什么格式

得到后一般若有video stream,则以video为參考来计算流信息(正确的做法是找到流的pcr_pid,也就是參考流来计算,但普通情况下參考流都是video)

计算duration: 首先找到第一帧的pts , 然后找到最后一帧的pts, 通过pts差距得到duration

计算bitrate: 有了stream size和 duration, 直接计算得到bitrate

当中计算pts的时候,这里介绍下,详细代码在ts/stream.c中,是后面我自己加的

        

if ( packet -> payloadlen > 0 && packet -> unitstart )

     {

         uint8_t * pcrbuf = packet -> payload ;

         int len = pcrbuf [ 0 ];

        if ( len <= 0 || len > 183 ) //broken from the stream layer or invalid

             goto QUIT ;

         pcrbuf ++ ;

int flags = pcrbuf [ 0 ];

int has_pcr ;

has_pcr = flags & 0x10 ;

         pcrbuf ++ ;

         if ( ! has_pcr )

             goto QUIT ;

         int64_t pcr = - 1 ;

         int64_t pcr_ext = - 1 ;

         unsigned int v = 0 ;

    //v = (uint32_t)pcrbuf[3]<<24 | pcrbuf[2]<<16 | pcrbuf[1]<<8 |pcrbuf[0];

v = ( uint32_t ) pcrbuf [ 0 ] << 24 | pcrbuf [ 1 ] << 16 | pcrbuf [ 2 ] << 8 | pcrbuf [ 3 ];

         pcr = (( int64_t ) v << 1 ) | ( pcrbuf [ 4 ] >> 7 );

pcr_ext = ( pcrbuf [ 4 ] & 0x01 ) << 8 ;

pcr_ext |= pcrbuf [ 5 ];

        pcr = pcr * 300 + pcr_ext

         packet -> pts = pcr / 300 ;

         //printf("get pts:%lld \n",pcr);

     }


这里是去掉了ts包开头的四个字节,首先看是否是一帧的开头,若是的话推断是否有pcr flag

若有则直接解析,解析方法比較简单,依照标准就可以

这里介绍下ts的编码參考时钟为:27M HZ,而pts的单位与时间s的换算为:90000

因此获取參考时间(也就是上面的pcr后),换算为pts的计算方式为: pts = pcr * 9000 / 27000000 = pcr / 300

这里得到的就直接是pts了。


另一个问题是:这里直接解析es流貌似不能获取 视频: width height  音频:channel samplerate等信息,

对于ffmpeg来讲没有问题,因此在av_find_stream_info中能够通过decode one frame来获取, 但dtplayer框架上是不方便直接启动解码器的

对于mplayer 的ts 也是没有decode的, 知道的同学能够指导下最好,不胜感激 ,否则得自己扒mplayer的代码了。


3 ts_setup_info

这里根据ts_open获取的信息,来setup dtplayer的media info

然后选择av视频流就能够了,比較简单


4 ts_read_frame

这里比較重要,也是花了比較多时间的地方,一開始的时候,以为拿到av的pid后,后面直接组装数据就能够了

流程为: 解析ts包获取pid -> 若是选择的pid,直接将payload保存下来,通过简单的推断是否是unit_start来推断是否读取到了完整帧 --> 返回完整帧

但实际情况并非这样,保存在ts包中的是实际的pes包,而不是es流,在读包的时候须要将pes包头去掉


这里參考ffmpeg进行了模拟,详细代码在: handle_ts_pkt 中,详细逻辑不说了,仅仅是更正自己的一个认知误区

只是这里还有个问题,读取的数据包会掺杂错误信息: 经过跟代码,大体定位了问题,在解析pes包头的时候,是能够知道这个包的大小的

但读取ts包的时候都是依照188,即payload通常是184字节,这样非常有可能就超过了pes包的大小

此时应该怎样处理: 若仅仅读取固定大小凑足包大小返回,则声音是错的

若将全部payload都打包进去,则播放过程会有杂音。

这里还须要研究下。 有熟悉的同学也请指教


5 ts_seek

这里seek就比較简单了,尽管还未完毕,但思想能够说下,參考ffmpeg,一句bitrate seek到某个位置,读取ts包,计算pts

若不匹配,则依照二分查找算法,继续seek

来达到seek的目的。(后面会实现)


这里ts demux仅仅是为了实现一个简单的功能,给后面基于dtplayer开发简单的应用的开发人员提供便利: 若服务端也自己做的话,能够非常方便的改造dtplayer中的ts demux达到字节的要求,

同一时候去掉了ffmpeg的负担,岂不非常方便,这也是dtplayer会一直遵循的目标。


github: https://github.com/avplayer/dtplayer    # C++

github https://github.com/peterfuture/dtplayer  # C

bug report:  peter_future@outlook.com

blog:  http://blog.csdn.net/dtplayer

bbs:  http://avboost.com/

wiki:  http://wiki.avplayer.org/Dtplayer


因为后面随着开发的进行文章会进行细节的更新,因此为了保证读者随时读到最新的内容,文章禁止转载,多谢大家支持!















ts demuxer的加入记录


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论