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.5 0: 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\r\nx-NAT:%s:%d\r\n", 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){
。。。。。。。。。。。。。。。。。


欧拉,你看到播放效果了吗,亲^_^
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值