zoukankan      html  css  js  c++  java
  • FFMPEG实现带NAT特性IPTV的播放

    开发环境及工具: ubuntu 12.04 32位机器 wireshark
    知识点:NAT,网络地址转换,主要是为了实现地址复用的一个玩意,对接服务器是ZTE的,给了个很老的NAT文档,基本没啥用,还是靠抓包,已有的能播放的播放器的抓包如下(点播),
    协议交互:

    DESCRIBE rtsp://58.223.255.214:554/vod/84136900020005436260.mpg?userid=adtest1&stbip=114.221.131.188&clienttype=1&mediaid=0000000003020005244432&ifcharge=1&time=20130424102533+08&life=172800&usersessionid=2875009&vcdnid=vcdn001&boid=001&srcboid=001&columnid=1000000E&backupagent=58.223.255.214:554&ctype=1&playtype=0&Drm=0&EpgId=epg_nj_001&programid=84136900020005436260&contname=&fathercont=&bp=0&authid=5703268124&tscnt=0&tstm=0&tsflow=0&ifpricereqsnd=1&nodelevel=3&usercharge=723A0360F38E98FA21A457BE1AB9A8DB RTSP/1.0
    CSeq: 1
    User-Agent: CTC RTSP 1.0
    x-zmssFecCDN: yes
    x-NAT:192.168.1.145:43445  --------------------------------rtsp信令连接的地址
    x-zmssRtxSdp: yes
    x-Index:
     
    RTSP/1.0 302 Found   ---------------------------------------重定向跑到location
    Server: ZXUSS100 1.0
    Location: rtsp://180.96.175.84:554/vod/84136900020005436260.mpg?userid=adtest1&mediaid=0000000003020005244432&ifcharge=1&clienttype=1&time=20130424102533+08&life=172800&usersessionid=2875009&ifpricereqsnd=1&Bp=0&vcdnid=vcdn001&Drm=0&ctype=1&boid=001&columnid=1000000E&fathercont=&playtype=0&stbip=114.221.131.188&srcboid=001&backupagent=58.223.255.214:554&EpgId=epg_nj_001&contname=&tscnt=0&tstm=0&tsflow=0&authid=5703268124&programid=84136900020005436260&usercharge=6F5039A17DCFE4C5FE3DBA006F288BB4&nodelevel=2&orignode=unit09111309453150&whyrt=3
    CSeq: 1

     
    DESCRIBE rtsp://180.96.175.84:554/vod/84136900020005436260.mpg?userid=adtest1&mediaid=0000000003020005244432&ifcharge=1&clienttype=1&time=20130424102533+08&life=172800&usersessionid=2875009&ifpricereqsnd=1&Bp=0&vcdnid=vcdn001&Drm=0&ctype=1&boid=001&columnid=1000000E&fathercont=&playtype=0&stbip=114.221.131.188&srcboid=001&backupagent=58.223.255.214:554&EpgId=epg_nj_001&contname=&tscnt=0&tstm=0&tsflow=0&authid=5703268124&programid=84136900020005436260&usercharge=6F5039A17DCFE4C5FE3DBA006F288BB4&nodelevel=2&orignode=unit09111309453150&whyrt=3 RTSP/1.0
    CSeq: 2
    User-Agent: CTC RTSP 1.0
    x-zmssFecCDN: yes
    x-NAT:192.168.1.145:35317
    x-zmssRtxSdp: yes
    x-Index:

    RTSP/1.0 200 OK
    Server: ZXUSS100 1.0
    Cache-Control: must-revalidate
    Content-Base: rtsp://180.96.175.84:554/vod/84136900020005436260.mpg/
    Content-Length: 311
    Content-Type: application/sdp
    CSeq: 2
    Date: Wed, 24 Apr 2013 02:25:33 GMT
    Expires: Wed, 24 Apr 2013 02:25:33 GMT
    v=0
    o=- 1929210747 0 IN IP4 0.0.0.0
    s=ZMSS RTSP Server
    c=IN IP4 0.0.0.0
    b=AS:1600 
    t=0 0
    a=control:*
    a=range:npt=0.00000-6013.91016
    m=video 0 RTP/AVPF 33 96
    a=control:trackID=1
    a=rtpmap:33 MP2T/90000
    a=3GPP-Adaptation-Support:5
    a=rtcp-fb:33 nack
    a=rtpmap:96 rtx/90000
    a=fmtp:96 apt=33;rtx-time=0
     
    SETUP rtsp://180.96.175.84:554/vod/84136900020005436260.mpg/ RTSP/1.0
    CSeq: 3
    Transport: MP2T/RTP/UDP;unicast;destination=192.168.1.145;client_port=65140-65141,MP2T/TCP;unicast;destination=192.168.1.145;interleaved=0-1,MP2T/RTP/TCP;unicast;destination=192.168.1.145;interleaved=0-1,MP2T/UDP;unicast;destination=192.168.1.145;client_port=65140-65141
    User-Agent: CTC RTSP 1.0
    x-NAT:192.168.1.145:65140------------------------------真实接收数据流的地址

    RTSP/1.0 200 OK
    Server: ZXUSS100 1.0
    x-KeepAliveInterval: 5000  --------------------------------打洞心跳
    CSeq: 3
    Date: Wed, 24 Apr 2013 02:25:33 GMT
    Expires: Wed, 24 Apr 2013 02:25:33 GMT
    Session: 196609438
    Transport: MP2T/RTP/UDP;unicast;destination=192.168.1.145;client_port=65140-65141;server_port=11656-11657;source=180.96.175.50

    PLAY rtsp://180.96.175.84:554/vod/84136900020005436260.mpg?userid=adtest1&mediaid=0000000003020005244432&ifcharge=1&clienttype=1&time=20130424102533+08&life=172800&usersessionid=2875009&ifpricereqsnd=1&Bp=0&vcdnid=vcdn001&Drm=0&ctype=1&boid=001&columnid=1000000E&fathercont=&playtype=0&stbip=114.221.131.188&srcboid=001&backupagent=58.223.255.214:554&EpgId=epg_nj_001&contname=&tscnt=0&tstm=0&tsflow=0&authid=5703268124&programid=84136900020005436260&usercharge=6F5039A17DCFE4C5FE3DBA006F288BB4&nodelevel=2&orignode=unit09111309453150&whyrt=3 RTSP/1.0
    CSeq: 4
    Session: 196609438--------------------------------------------关键信息,服务器靠它来判断是哪个用户发来的打洞消息
    Scale: 1.000000
    Range: npt=0.000-
    User-Agent: CTC RTSP 1.0

    RTSP/1.0 200 OK
    Server: ZXUSS100 1.0
    x-KeepAliveInterval: 5000
    CSeq: 4
    Range: npt=0.00000-
    Scale: 1.0
    Session: 196609438
    RTP-Info: url=rtsp://180.96.175.50:11656/vod/84136900020005436260.mpg/trackID=1
    192.168.1.145:65140==》180.96.175.50:11656打洞消息如下:
    改UDP消息格式是:常量(ZXV10STB)+sessionid+客户端IP+ 接收数据流端口+信令连接端口,关键信息是sessionid
    00000000  5a 58 56 31 30 53 54 42  0b b8 05 9e c0 a8 01 91 ZXV10STB ........
    00000010  fe 74 89 f5 00 00 00 00  00 00 00 00 00 00 00 00 .t...... ........
    00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ........ ........
    00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ........ ........
    00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ........ ........
    00000050  00 00 00 00   
     
    ffmpeg对rtsp支持的不是很好,建议先调测live555,让整个播放流程走通,再对比抓包修改ffmpeg代码。
    ffmpeg代码修改知识点:
    a、ffmpeg对于rtsp协议会首先发送option心跳消息,而实际应用上没有免费的午餐,用户必须先通过describe消息进行认证,
          因此ff_rtsp_connect函数要进行修改
    #if 0
            ff_rtsp_send_cmd(s, "OPTIONS", rt->control_uri, cmd, reply, NULL);
            if (reply->status_code != RTSP_STATUS_OK) {
                err = AVERROR_INVALIDDATA;
                goto fail;
            }
    #endif

    b、要支持NAT,describe消息中要携带nat信息,函数ff_rtsp_setup_input_streams 要修改

        int rtspfd = rt->rtsp_hd_out->fd;
        int len = sizeof(struct sockaddr_in);
        struct sockaddr_in sin;
        if ( getsockname ( rtspfd, (struct sockaddr *)&sin, (socklen_t *)&len ) == 0 )
            INFO("socket port number: %d", ntohs ( sin.sin_port ) );

        /* describe the stream */
        snprintf(cmd, sizeof(cmd),
                 "Accept: application/sdp x-NAT:%s:%d ", inet_ntoa(sin.sin_addr), ntohs ( sin.sin_port ));

    c、传输协议类型,ffmpeg要明确表明支持MP2T/RTP(其实就是ts),否则那个傻服务器会给你461哦

    d、打洞消息,在信令链路建立成功后要记得进行NAT穿透最关键的一步,打通隧道(打洞)

         int send_nat_data(AVFormatContext *s)
    {
        int i;
        int len = sizeof(struct sockaddr_in);   
        struct sockaddr_in sin;     
        RTSPState *rt = s->priv_data;
        if ( getsockname (rt->rtsp_hd_out->fd, (struct sockaddr*)&sin, (socklen_t *)&len ) == 0 )       
            ;//INFO("socket port number: %s, %d", inet_ntoa(sin.sin_addr),ntohs ( sin.sin_port ) );
        unsigned char buf[128] = {0};

        int sesstionid = atoi(rt->session_id);
        unsigned char sessionstr[5] = {0};
        sessionstr[0] = sesstionid >> 24;
        sessionstr[1] = (sesstionid >> 16) & 0xff;
        sessionstr[2] = (sesstionid >> 8) & 0xff;
        sessionstr[3] = sesstionid &0xff;
        
        unsigned int addrid = ntohl(sin.sin_addr.s_addr);
        unsigned char addrstr[5] = {0};
        addrstr[0] = addrid >> 24;
        addrstr[1] = (addrid >> 16) & 0xff;
        addrstr[2] = (addrid >> 8) & 0xff;
        addrstr[3] = addrid & 0xff;
        
        for ( i = 0; i < rt->nb_rtsp_streams; ++i) {
            RTSPStream *rtsp_st = rt->rtsp_streams[i];
            unsigned short udpportid = ff_rtp_get_local_rtp_port(rtsp_st->rtp_handle);
            unsigned char udpportstr[3] = {0};
            udpportstr[0] = udpportid >> 8;
            udpportstr[1] = udpportid & 0xff;

            unsigned short tcpportid = ntohs(sin.sin_port);
            unsigned char tcpportstr[3] = {0};
            tcpportstr[0] = tcpportid >> 8;
            tcpportstr[1] = tcpportid & 0xff;

            snprintf((char *)buf,sizeof(buf), "ZXV10STB%s%s%s%s", 
                sessionstr, addrstr, udpportstr, tcpportstr);        
            int ret = ffurl_write(rtsp_st->rtp_handle, buf, 84);
        }
        return 0;
    }

    e、nat穿透是UDP的包,注意哦,是UDP,容易走丢,对没错。所以在收不到数据包的时候要继续打洞哦

    static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st,
                               uint8_t *buf, int buf_size, int64_t wait_end)
    {
    。。。。。。。。。。。
            } else if (n == 0) {
                if(++timeout_cnt >= MAX_TIMEOUTS)
                    return AVERROR(ETIMEDOUT);
                send_nat_data(s);
            } else if (n < 0 && errno != EINTR){
    。。。。。。。。。。。。。。。。。


    欧拉,你看到播放效果了吗,亲^_^

    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    03 Spring框架 bean的属性以及bean前处理和bean后处理
    02 Spring框架 简单配置和三种bean的创建方式
    01 Spring框架 基本介绍
    Struts2学习---拦截器+struts的工作流程+struts声明式异常处理
    js 正则表达式
    HTTP中GET与POST的区别 (本质上是一样一样的TCP链接)
    微信商户平台的“企业付款到用户” 产品功能被隐藏起来了。。
    软件的安装及下载
    phpstorm 使用技巧
    Socket.io和Redis写Realtime App 之express初试
  • 原文地址:https://www.cnblogs.com/agiletiger/p/4888253.html
Copyright © 2011-2022 走看看