VLC播放RTP打包发送的.264文件

1,要有一个发送RTP包的264文件的服务器;

具体代码如下:

rtp.h

#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib") #define PACKET_BUFFER_END (unsigned int)0x00000000
#define MAX_RTP_PKT_LENGTH 1400
#define DEST_IP "172.18.191.194"
#define DEST_PORT 554
#define H264 96 typedef struct
{
/**//* byte 0 */
unsigned char csrc_len:; /**//* expect 0 */
unsigned char extension:; /**//* expect 1, see RTP_OP below */
unsigned char padding:; /**//* expect 0 */
unsigned char version:; /**//* expect 2 */
/**//* byte 1 */
unsigned char payload:; /**//* RTP_PAYLOAD_RTSP */
unsigned char marker:; /**//* expect 1 */
/**//* bytes 2, 3 */
unsigned short seq_no;
/**//* bytes 4-7 */
unsigned long timestamp;
/**//* bytes 8-11 */
unsigned long ssrc; /**//* stream number is used here. */
} RTP_FIXED_HEADER; typedef struct {
//byte 0
unsigned char TYPE:;
unsigned char NRI:;
unsigned char F:; } NALU_HEADER; /**//* 1 BYTES */ typedef struct {
//byte 0
unsigned char TYPE:;
unsigned char NRI:;
unsigned char F:; } FU_INDICATOR; /**//* 1 BYTES */ typedef struct {
//byte 0
unsigned char TYPE:;
unsigned char R:;
unsigned char E:;
unsigned char S:;
} FU_HEADER; /**//* 1 BYTES */

rtp.cpp

#include "rtp.h"
#include <stdio.h> FILE * bits = NULL; //!< the bit stream file
static int FindStartCode2 (unsigned char *Buf);//查找开始字符0x000001
static int FindStartCode3 (unsigned char *Buf);//查找开始字符0x00000001
//static bool flag = true;
static int info2=, info3=;
RTP_FIXED_HEADER *rtp_hdr; NALU_HEADER *nalu_hdr;
FU_INDICATOR *fu_ind;
FU_HEADER *fu_hdr; typedef struct
{
int startcodeprefix_len; //! 4 for parameter sets and first slice in picture, 3 for everything else (suggested)
unsigned len; //! Length of the NAL unit (Excluding the start code, which does not belong to the NALU)
unsigned max_size; //! Nal Unit Buffer size
int forbidden_bit; //! should be always FALSE
int nal_reference_idc; //! NALU_PRIORITY_xxxx
int nal_unit_type; //! NALU_TYPE_xxxx
char *buf; //! contains the first byte followed by the EBSP
unsigned short lost_packets; //! true, if packet loss is detected
} NALU_t; BOOL InitWinsock()
{
int Error;
WORD VersionRequested;
WSADATA WsaData;
VersionRequested=MAKEWORD(,);
Error=WSAStartup(VersionRequested,&WsaData); //启动WinSock2
if(Error!=)
{
return FALSE;
}
else
{
if(LOBYTE(WsaData.wVersion)!=||HIBYTE(WsaData.wHighVersion)!=)
{
WSACleanup();
return FALSE;
} }
return TRUE;
} //为NALU_t结构体分配内存空间
NALU_t *AllocNALU(int buffersize)
{
NALU_t *n; if ((n = (NALU_t*)calloc (, sizeof (NALU_t))) == NULL)
{
printf("AllocNALU: n");
exit();
} n->max_size=buffersize; if ((n->buf = (char*)calloc (buffersize, sizeof (char))) == NULL)
{
free (n);
printf ("AllocNALU: n->buf");
exit();
} return n;
} //释放
void FreeNALU(NALU_t *n)
{
if (n)
{
if (n->buf)
{
free(n->buf);
n->buf=NULL;
}
free (n);
}
} //这个函数输入为一个NAL结构体,主要功能为得到一个完整的NALU并保存在NALU_t的buf中,获取他的长度,填充F,IDC,TYPE位。
//并且返回两个开始字符之间间隔的字节数,即包含有前缀的NALU的长度
int GetAnnexbNALU (NALU_t *nalu)
{
int pos = ;
int StartCodeFound, rewind;
unsigned char *Buf; if ((Buf = (unsigned char*)calloc (nalu->max_size , sizeof(char))) == NULL)
printf ("GetAnnexbNALU: Could not allocate Buf memory\n"); nalu->startcodeprefix_len=;//初始化码流序列的开始字符为3个字节 if ( != fread (Buf, , , bits))//从码流中读3个字节
{
free(Buf);
return ;
}
info2 = FindStartCode2 (Buf);//判断是否为0x000001
if(info2 != )
{
//如果不是,再读一个字节
if( != fread(Buf+, , , bits))//读一个字节
{
free(Buf);
return ;
}
info3 = FindStartCode3 (Buf);//判断是否为0x00000001
if (info3 != )//如果不是,返回-1
{
free(Buf);
return -;
}
else
{
//如果是0x00000001,得到开始前缀为4个字节
pos = ;
nalu->startcodeprefix_len = ;
}
} else
{
//如果是0x000001,得到开始前缀为3个字节
nalu->startcodeprefix_len = ;
pos = ;
}
//查找下一个开始字符的标志位
StartCodeFound = ;
info2 = ;
info3 = ; while (!StartCodeFound)
{
if (feof (bits))//判断是否到了文件尾
{
nalu->len = (pos-)-nalu->startcodeprefix_len;
memcpy (nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len);
nalu->forbidden_bit = nalu->buf[] & 0x80; //1 bit
nalu->nal_reference_idc = nalu->buf[] & 0x60; // 2 bit
nalu->nal_unit_type = (nalu->buf[]) & 0x1f;// 5 bit
free(Buf);
return pos-;
}
Buf[pos++] = fgetc (bits);//读一个字节到BUF中
info3 = FindStartCode3(&Buf[pos-]);//判断是否为0x00000001
if(info3 != )
info2 = FindStartCode2(&Buf[pos-]);//判断是否为0x000001
StartCodeFound = (info2 == || info3 == );
} // Here, we have found another start code (and read length of startcode bytes more than we should
// have. Hence, go back in the file
rewind = (info3 == )? - : -; if ( != fseek (bits, rewind, SEEK_CUR))//把文件指针指向前一个NALU的末尾
{
free(Buf);
printf("GetAnnexbNALU: Cannot fseek in the bit stream file");
} // Here the Start code, the complete NALU, and the next start code is in the Buf.
// The size of Buf is pos, pos+rewind are the number of bytes excluding the next
// start code, and (pos+rewind)-startcodeprefix_len is the size of the NALU excluding the start code nalu->len = (pos+rewind)-nalu->startcodeprefix_len;
memcpy (nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len);//拷贝一个完整NALU,不拷贝起始前缀0x000001或0x00000001
nalu->forbidden_bit = nalu->buf[] & 0x80; //1 bit
nalu->nal_reference_idc = nalu->buf[] & 0x60; // 2 bit
nalu->nal_unit_type = (nalu->buf[]) & 0x1f;// 5 bit
free(Buf); return (pos+rewind);//返回两个开始字符之间间隔的字节数,即包含有前缀的NALU的长度
} //输出NALU长度和TYPE
void dump(NALU_t *n)
{
if (!n)return;
//printf("a new nal:");
printf(" len: %d ", n->len);
printf("nal_unit_type: %x\n", n->nal_unit_type);
} int main(int argc, char* argv[])
{
NALU_t * n;
char* nalu_payload;
char sendbuf[];
unsigned short seq_num =;
int bytes=; SOCKET socket1;
struct sockaddr_in server;
int len =sizeof(server);
float framerate=;
unsigned int timestamp_increase=,ts_current=;
timestamp_increase=(unsigned int)(90000.0 / framerate); //+0.5); bits=fopen("my.h264","rb");
InitWinsock(); //初始化套接字库 server.sin_family=AF_INET;
server.sin_port=htons(DEST_PORT);
server.sin_addr.s_addr=inet_addr(DEST_IP);
socket1=socket(AF_INET,SOCK_DGRAM,);
connect(socket1, (const struct sockaddr *)&server, len) ;//申请UDP套接字
n = AllocNALU();//为结构体nalu_t及其成员buf分配空间。返回值为指向nalu_t存储空间的指针 while(!feof(bits))
{
GetAnnexbNALU(n);//每执行一次,文件的指针指向本次找到的NALU的末尾,下一个位置即为下个NALU的起始码0x000001
dump(n);//输出NALU长度和TYPE memset(sendbuf,,);//清空sendbuf;此时会将上次的时间戳清空,因此需要ts_current来保存上次的时间戳值
//rtp固定包头,为12字节,该句将sendbuf[0]的地址赋给rtp_hdr,以后对rtp_hdr的写入操作将直接写入sendbuf。
rtp_hdr =(RTP_FIXED_HEADER*)&sendbuf[];
//设置RTP HEADER,
rtp_hdr->payload = H264; //负载类型号,
rtp_hdr->version = ; //版本号,此版本固定为2
rtp_hdr->marker = ; //标志位,由具体协议规定其值。
rtp_hdr->ssrc = htonl(); //随机指定为10,并且在本RTP会话中全局唯一 // 当一个NALU小于1400字节的时候,采用一个单RTP包发送
if(n->len<=)
{
//设置rtp M 位;
rtp_hdr->marker=;
rtp_hdr->seq_no = htons(seq_num ++); //序列号,每发送一个RTP包增1
//设置NALU HEADER,并将这个HEADER填入sendbuf[12]
nalu_hdr =(NALU_HEADER*)&sendbuf[]; //将sendbuf[12]的地址赋给nalu_hdr,之后对nalu_hdr的写入就将写入sendbuf中;
nalu_hdr->F=n->forbidden_bit;
nalu_hdr->NRI=n->nal_reference_idc>>;//有效数据在n->nal_reference_idc的第6,7位,需要右移5位才能将其值赋给nalu_hdr->NRI。
nalu_hdr->TYPE=n->nal_unit_type; nalu_payload=&sendbuf[];//同理将sendbuf[13]赋给nalu_payload
memcpy(nalu_payload,n->buf+,n->len-);//去掉nalu头的nalu剩余内容写入sendbuf[13]开始的字符串。 ts_current=ts_current+timestamp_increase;
rtp_hdr->timestamp=htonl(ts_current);
bytes=n->len + ; //获得sendbuf的长度,为nalu的长度(包含NALU头但除去起始前缀)加上rtp_header的固定长度12字节
send( socket1, sendbuf, bytes, );//发送rtp包
//sleep(1);
//fwrite(sendbuf,bytes, 1, stream);
} else if(n->len>)
{
//得到该nalu需要用多少长度为1400字节的RTP包来发送
int k=,l=;
k=n->len/;//需要k个1400字节的RTP包
l=n->len%;//最后一个RTP包的需要装载的字节数
int t=;//用于指示当前发送的是第几个分片RTP包
ts_current=ts_current+timestamp_increase;
rtp_hdr->timestamp=htonl(ts_current);
while(t<=k)
{
rtp_hdr->seq_no = htons(seq_num ++); //序列号,每发送一个RTP包增1
if(!t)//发送一个需要分片的NALU的第一个分片,置FU HEADER的S位
{
//设置rtp M 位;
rtp_hdr->marker=;
//设置FU INDICATOR,并将这个HEADER填入sendbuf[12]
fu_ind =(FU_INDICATOR*)&sendbuf[]; //将sendbuf[12]的地址赋给fu_ind,之后对fu_ind的写入就将写入sendbuf中;
fu_ind->F=n->forbidden_bit;
fu_ind->NRI=n->nal_reference_idc>>;
fu_ind->TYPE=; //设置FU HEADER,并将这个HEADER填入sendbuf[13]
fu_hdr =(FU_HEADER*)&sendbuf[];
fu_hdr->E=;
fu_hdr->R=;
fu_hdr->S=;
fu_hdr->TYPE=n->nal_unit_type; nalu_payload=&sendbuf[];//同理将sendbuf[14]赋给nalu_payload
memcpy(nalu_payload,n->buf+,);//去掉NALU头 bytes=+; //获得sendbuf的长度,为nalu的长度(除去起始前缀和NALU头)加上rtp_header,fu_ind,fu_hdr的固定长度14字节
send( socket1, sendbuf, bytes, );//发送rtp包
//fwrite(sendbuf,bytes, 1, stream);
//sleep(1);
t++; }
//发送一个需要分片的NALU的非第一个分片,清零FU HEADER的S位,如果该分片是该NALU的最后一个分片,置FU HEADER的E位
else if(k==t)//发送的是最后一个分片,注意最后一个分片的长度可能超过1400字节(当l>1386时)。
{ //设置rtp M 位;当前传输的是最后一个分片时该位置1
rtp_hdr->marker=;
//设置FU INDICATOR,并将这个HEADER填入sendbuf[12]
fu_ind =(FU_INDICATOR*)&sendbuf[]; //将sendbuf[12]的地址赋给fu_ind,之后对fu_ind的写入就将写入sendbuf中;
fu_ind->F=n->forbidden_bit;
fu_ind->NRI=n->nal_reference_idc>>;
fu_ind->TYPE=; //设置FU HEADER,并将这个HEADER填入sendbuf[13]
fu_hdr =(FU_HEADER*)&sendbuf[];
fu_hdr->R=;
fu_hdr->S=;
fu_hdr->TYPE=n->nal_unit_type;
fu_hdr->E=; nalu_payload=&sendbuf[];//同理将sendbuf[14]的地址赋给nalu_payload
memcpy(nalu_payload,n->buf+t*+,l-);//将nalu最后剩余的l-1(去掉了一个字节的NALU头)字节内容写入sendbuf[14]开始的字符串。
bytes=l-+; //获得sendbuf的长度,为剩余nalu的长度l-1加上rtp_header,FU_INDICATOR,FU_HEADER三个包头共14字节
send( socket1, sendbuf, bytes, );//发送rtp包
//fwrite(sendbuf,bytes, 1, stream);
t++;
//sleep(1);
}
else if(t<k&&!=t)
{
//设置rtp M 位;
rtp_hdr->marker=;
//设置FU INDICATOR,并将这个HEADER填入sendbuf[12]
fu_ind =(FU_INDICATOR*)&sendbuf[]; //将sendbuf[12]的地址赋给fu_ind,之后对fu_ind的写入就将写入sendbuf中;
fu_ind->F=n->forbidden_bit;
fu_ind->NRI=n->nal_reference_idc>>;
fu_ind->TYPE=; //设置FU HEADER,并将这个HEADER填入sendbuf[13]
fu_hdr =(FU_HEADER*)&sendbuf[];
//fu_hdr->E=0;
fu_hdr->R=;
fu_hdr->S=;
fu_hdr->E=;
fu_hdr->TYPE=n->nal_unit_type; nalu_payload=&sendbuf[];//同理将sendbuf[14]的地址赋给nalu_payload
memcpy(nalu_payload,n->buf+t*+,);//去掉起始前缀的nalu剩余内容写入sendbuf[14]开始的字符串。
bytes=+; //获得sendbuf的长度,为nalu的长度(除去原NALU头)加上rtp_header,fu_ind,fu_hdr的固定长度14字节
send( socket1, sendbuf, bytes, );//发送rtp包
//fwrite(sendbuf,bytes, 1, stream);
//sleep(1);
t++;
}
}
}
//usleep(40000);
}
FreeNALU(n); return ;
} static int FindStartCode2 (unsigned char *Buf)
{
if(Buf[]!= || Buf[]!= || Buf[] !=) return ; //判断是否为0x000001,如果是返回1
else return ;
} static int FindStartCode3 (unsigned char *Buf)
{
if(Buf[]!= || Buf[]!= || Buf[] != || Buf[] !=) return ;//判断是否为0x00000001,如果是返回1
else return ;
}

2,下载vlc;

3,进行如下设置:
1>设置w.sdp
a,打开vlc->Media->Open network stream
b,-->network->rtp://@192.168.1.101:1234
c,-->file->add...w.sdp->play
这里要讲一下sdp文件,sdp文件时vlc播放是需要的文件,这里是必须的,否则是无法播放的,w.sdp文件其实只是几句话,这里我贴出来:

m=video  RTP/AVP  //96表示的是动态净荷类型号 意思就是负载类型不确定,要通过其他方式来确定
a=rtpmap: H264
a=framerate:
c=IN IP4 192.168.0.30

上面的1234和192.168.1.101就是RTP的端口和IP

2〉点击vlc播放按钮 
3〉再运行264文件的RTP包发送的服务器
4〉一会儿就看看vlc播放视频了

参考:

1,VLC播放RTP打包发送的.264文件

http://blog.csdn.net/liuzongming1988/article/details/8292455

(转)VLC播放RTP打包发送的.264文件的更多相关文章

  1. VLC播放RTSP视频延迟问题

    VLC播放RTSP视频延迟问题 配置 VLC 以播放 RTSP/RTP 流 实测发现RTP都不如TCP快? vlc播放rtp封装的h.264延时很大是什么原因? 开启打印: VLC的工具->消息 ...

  2. (转)live555学习笔记7-RTP打包与发送

    七 RTP打包与发送 rtp传送开始于函数:MediaSink::startPlaying().想想也有道理,应是sink跟source要数据,所以从sink上调用startplaying(嘿嘿,相当 ...

  3. 【转】实现RTP协议的H.264视频传输系统

    1.  引言       随着信息产业的发展,人们对信息资源的要求已经逐渐由文字和图片过渡到音频和视频,并越来越强调获取资源的实时性和互动性.但人们又面临着另外一种不可避免的尴尬,就是在网络上看到生动 ...

  4. 基于RTP协议的H.264传输

    1.  引言        随 着信息产业的发展,人们对信息资源的要求已经逐渐由文字和图片过渡到音频和视频,并越来越强调获取资源的实时性和互动性.但人们又面临着另外一种不可避免 的尴尬,就是在网络上看 ...

  5. 多媒体开发之rtp打包---打包中的FU-A分包方式说明

    继上篇rtp中的时间戳和负载类型之后,升入到了nalu的分片打包问题,这里做下笔记 (1)fu-a的打包格式 1.基于RTP协议的打包及解包 (1)单个NAL打包 H.264NALU单元常由[star ...

  6. Centos 上使用mmsh协议听猫扑网络电台 VLC播放器

    Centos 上使用mmsh协议听猫扑网络电台 VLC播放器 安装CentOS已经有一段时间了,但是由于在Linux下除了学习,其他是事情都干不了.今天想闲来无事开了CentOS就想听一下歌,突然想起 ...

  7. 庖丁解牛-----Live555源码彻底解密(RTP打包)

    本文主要讲解live555的服务端RTP打包流程,根据MediaServer讲解RTP的打包流程,所以大家看这篇文章时,先看看下面这个链接的内容; 庖丁解牛-----Live555源码彻底解密(根据M ...

  8. C++实现RTMP协议发送H.264编码及AAC编码的音视频

    http://www.cnblogs.com/haibindev/archive/2011/12/29/2305712.html C++实现RTMP协议发送H.264编码及AAC编码的音视频 RTMP ...

  9. C++实现RTMP协议发送H.264编码及AAC编码的音视频(转)

    C++实现RTMP协议发送H.264编码及AAC编码的音视频(转) RTMP(Real Time Messaging Protocol)是专门用来传输音视频数据的流媒体协议,最初由Macromedia ...

随机推荐

  1. 阿里正式发布《Java开发手册》终极版!

    摘要: 本文讲的是阿里正式发布<Java开发手册>终极版!,别人都说我们是码农,但我们知道,自己是个艺术家.也许我们不过多在意自己的外表和穿着,但我们不羁的外表下,骨子里追求着代码的美.质 ...

  2. linux学习笔记28--监控命令vmstat,iostat, lsof

    linux的监控包括多个方面,常用的是进程,内存,I/O,磁盘空间这三个方面. vmstat是Virtual Meomory Statistics(虚拟内存统计)的缩写,可对操作系统的虚拟内存.进程. ...

  3. matlab和c++混合编程---matlab和vs的环境配置问题及方法和步骤(转载)

    matlab和c++混合编程---方法和步骤 matlab和c++混合编程---matlab和vs的环境配置问题 摘要:Matlab具有很强的数值计算和分析等能力,而C/C++是目前最为流行的高级程序 ...

  4. C++11 中function和bind以及lambda 表达式的用法

    关于std::function 的用法:  其实就可以理解成函数指针 1. 保存自由函数 void printA(int a) { cout<<a<<endl; } std:: ...

  5. Java动态代理原理及其简单应用

    概念 代理对象和被代理对象一般实现相同的接口,调用者与代理对象进行交互.代理的存在对于调用者来说是透明的,调用者看到的只是接口.代理对象则可以封装一些内部的处理逻辑,如访问控制.远程通信.日志.缓存等 ...

  6. 【iOS越狱开发】如何将应用打包成.ipa文件

    在项目开发中,我们常常需要将工程文件打包成.ipa文件,提供给越狱的iphone安装. 下面是一种方法: 1.首先应该给工程安装好配置文件(这里不再敖述),在ios device的状态下,运行成功. ...

  7. 【★】深入BGP原理和思想【第…

    前言:学思科技术我想说,浅尝辄止,不是天才千万别深钻.和我研究高等数学一样,越深入就会发现越多的问题与不合理之处.尤其对于IT界,算法的最终解释权还是掌握在老外手中,所以对于有些细节,我们" ...

  8. 【BZOJ】1827: [Usaco2010 Mar]gather 奶牛大集会(树形dp)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1827 仔细想想就好了,, 每个点维护两个值,一个是子树的费用,一个是除了子树和自己的费用.都可以用d ...

  9. The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Cha

    The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Cha ...

  10. Asp.net控制Tomcat启动关闭的实现方法

    一.场景 近日有个项目客户要求能自己配置相关权限.由于历史原因这个项目采用的是公司以前的权限系统.这个权限系统很强大,不过有个弊端,就是每增加一个权限菜单都要重启才能生效,不然就要等1天它缓存过期后才 ...