前段时间写了个测试程序,使用jrtplib,把h264打成标准的rtp数据包,并能通过vlc,mpalyer等播放器播放出来。这中间主要的难点是解析h264数据中的nal单元,并把nal单元打成rtp包,之后的事情都是jrtplib库做了。然后就可以通过播放器播放出来了。

使用播放器播放时使用以下命令:

mplayer player.sdp

或者

用vlc打开player.sdp

对于rtp打包不懂的同学,可以仔细看下这个包里面的文档和一个rtp打包的代码,这是我上传的资源。

http://download.csdn.net/detail/xyyangkun/6990313

主要的代码贴出来:

/** test_jrtp.cpp**  Created on: 2014-2-19*      Author: xy*/#include "rtpsession.h"
#include "rtpsessionparams.h"
#include "rtpudpv4transmitter.h"
#include "rtpipv4address.h"
#include "rtptimeutilities.h"
#include "rtppacket.h"
#include <stdlib.h>
#include <iostream>
#include "h264.h"
#define SSRC           100#define DEST_IP_STR   "127.0.0.1"
#define DEST_PORT     9000
#define BASE_PORT     2222using namespace jrtplib;int main(int argc, char** argv)
{RTPSession session;RTPSessionParams sessionparams;sessionparams.SetOwnTimestampUnit(1.0/90000.0);RTPUDPv4TransmissionParams transparams;transparams.SetPortbase(8000);int status = session.Create(sessionparams,&transparams);if (status < 0){std::cerr << RTPGetErrorString(status) << std::endl;exit(-1);}uint8_t localip[]={127,0,0,1};RTPIPv4Address addr(localip,9000);status = session.AddDestination(addr);if (status < 0){std::cerr << RTPGetErrorString(status) << std::endl;exit(-1);}session.SetDefaultPayloadType(96);session.SetDefaultMark(false);session.SetDefaultTimestampIncrement(90000.0 /25.0);RTPTime delay(0.040);RTPTime starttime = RTPTime::CurrentTime();NALU_HEADER      *nalu_hdr;FU_INDICATOR  *fu_ind;FU_HEADER       *fu_hdr;char sendbuf[1500];char* nalu_payload;unsigned int timestamp_increse=0,ts_current=0;#define ddd//OpenBitstreamFile("agnt.264");//打开264文件,并将文件指针赋给bits,在此修改文件名实现打开别的264文件。OpenBitstreamFile("1.264");//打开264文件,并将文件指针赋给bits,在此修改文件名实现打开别的264文件。//OpenBitstreamFile("test.264");//打开264文件,并将文件指针赋给bits,在此修改文件名实现打开别的264文件。//OpenBitstreamFile("slamtv60.264");//打开264文件,并将文件指针赋给bits,在此修改文件名实现打开别的264文件。//OpenBitstreamFile("avc.h264");//打开264文件,并将文件指针赋给bits,在此修改文件名实现打开别的264文件。NALU_t *n;n = AllocNALU(8000000);//为结构体nalu_t及其成员buf分配空间。返回值为指向nalu_t存储空间的指针bool start=false;while(!feof(bits)){int size=GetAnnexbNALU(n);//每执行一次,文件的指针指向本次找到的NALU的末尾,下一个位置即为下个NALU的起始码0x000001if(size<4){printf("get nul error!\n");continue;}dump(n);//输出NALU长度和TYPEif(!start){if(n->nal_unit_type==5||n->nal_unit_type==6||n->nal_unit_type==7||n->nal_unit_type==7){printf("begin\n");start=true;}}//将编码数据写入文件t//fwrite(pNals[i].p_payload, 1, pNals[i].i_payload, pFile);//发送编码文件
#if 1// 当一个NALU小于MAX_RTP_PKT_LENGTH字节的时候,采用一个单RTP包发送if(n->len<=MAX_RTP_PKT_LENGTH){//printf("ddd0\n");//session.SetDefaultMark(false);//设置NALU HEADER,并将这个HEADER填入sendbuf[12]nalu_hdr =(NALU_HEADER*)&sendbuf[0]; //将sendbuf[12]的地址赋给nalu_hdr,之后对nalu_hdr的写入就将写入sendbuf中;nalu_hdr->F=n->forbidden_bit;nalu_hdr->NRI=n->nal_reference_idc>>5;//有效数据在n->nal_reference_idc的第6,7位,需要右移5位才能将其值赋给nalu_hdr->NRI。nalu_hdr->TYPE=n->nal_unit_type;nalu_payload=&sendbuf[1];//同理将sendbuf[13]赋给nalu_payloadmemcpy(nalu_payload,n->buf+1,n->len-1);//去掉nalu头的nalu剩余内容写入sendbuf[13]开始的字符串。ts_current=ts_current+timestamp_increse;//status = session.SendPacket((void *)sendbuf,n->len);if(n->nal_unit_type==1 || n->nal_unit_type==5){status = session.SendPacket((void *)sendbuf,n->len,96,true,3600);}else{status = session.SendPacket((void *)sendbuf,n->len,96,true,0);\//如果是6,7类型的包,不应该延时;之前有停顿,原因这在这continue;}//发送RTP格式数据包并指定负载类型为96if (status < 0){std::cerr << RTPGetErrorString(status) << std::endl;exit(-1);}}else if(n->len>MAX_RTP_PKT_LENGTH){//得到该nalu需要用多少长度为MAX_RTP_PKT_LENGTH字节的RTP包来发送int k=0,l=0;k=n->len/MAX_RTP_PKT_LENGTH;//需要k个MAX_RTP_PKT_LENGTH字节的RTP包l=n->len%MAX_RTP_PKT_LENGTH;//最后一个RTP包的需要装载的字节数int t=0;//用于指示当前发送的是第几个分片RTP包ts_current=ts_current+timestamp_increse;while(t<=k){if(!t)//发送一个需要分片的NALU的第一个分片,置FU HEADER的S位{//printf("dddd1");memset(sendbuf,0,1500);//session.SetDefaultMark(false);//设置FU INDICATOR,并将这个HEADER填入sendbuf[12]fu_ind =(FU_INDICATOR*)&sendbuf[0]; //将sendbuf[12]的地址赋给fu_ind,之后对fu_ind的写入就将写入sendbuf中;fu_ind->F=n->forbidden_bit;fu_ind->NRI=n->nal_reference_idc>>5;fu_ind->TYPE=28;//设置FU HEADER,并将这个HEADER填入sendbuf[13]fu_hdr =(FU_HEADER*)&sendbuf[1];fu_hdr->E=0;fu_hdr->R=0;fu_hdr->S=1;fu_hdr->TYPE=n->nal_unit_type;nalu_payload=&sendbuf[2];//同理将sendbuf[14]赋给nalu_payloadmemcpy(nalu_payload,n->buf+1,MAX_RTP_PKT_LENGTH);//去掉NALU头//status = session.SendPacket((void *)sendbuf,MAX_RTP_PKT_LENGTH+2);status = session.SendPacket((void *)sendbuf,MAX_RTP_PKT_LENGTH+2,96,false,0);if (status < 0){std::cerr << RTPGetErrorString(status) << std::endl;exit(-1);}t++;}//发送一个需要分片的NALU的非第一个分片,清零FU HEADER的S位,如果该分片是该NALU的最后一个分片,置FU HEADER的E位else if(k==t)//发送的是最后一个分片,注意最后一个分片的长度可能超过MAX_RTP_PKT_LENGTH字节(当l>1386时)。{//printf("dddd3\n");memset(sendbuf,0,1500);//session.SetDefaultMark(true);//设置FU INDICATOR,并将这个HEADER填入sendbuf[12]fu_ind =(FU_INDICATOR*)&sendbuf[0]; //将sendbuf[12]的地址赋给fu_ind,之后对fu_ind的写入就将写入sendbuf中;fu_ind->F=n->forbidden_bit;fu_ind->NRI=n->nal_reference_idc>>5;fu_ind->TYPE=28;//设置FU HEADER,并将这个HEADER填入sendbuf[13]fu_hdr =(FU_HEADER*)&sendbuf[1];fu_hdr->R=0;fu_hdr->S=0;fu_hdr->TYPE=n->nal_unit_type;fu_hdr->E=1;nalu_payload=&sendbuf[2];//同理将sendbuf[14]赋给nalu_payloadmemcpy(nalu_payload,n->buf+t*MAX_RTP_PKT_LENGTH+1,l-1);//将nalu最后剩余的l-1(去掉了一个字节的NALU头)字节内容写入sendbuf[14]开始的字符串。//status = session.SendPacket((void *)sendbuf,l+1);status = session.SendPacket((void *)sendbuf,l+1,96,true,3600);if (status < 0){std::cerr << RTPGetErrorString(status) << std::endl;exit(-1);}t++;//  Sleep(100);}else if(t<k&&0!=t){//printf("dddd2");memset(sendbuf,0,1500);//session.SetDefaultMark(false);//设置FU INDICATOR,并将这个HEADER填入sendbuf[12]fu_ind =(FU_INDICATOR*)&sendbuf[0]; //将sendbuf[12]的地址赋给fu_ind,之后对fu_ind的写入就将写入sendbuf中;fu_ind->F=n->forbidden_bit;fu_ind->NRI=n->nal_reference_idc>>5;fu_ind->TYPE=28;//设置FU HEADER,并将这个HEADER填入sendbuf[13]fu_hdr =(FU_HEADER*)&sendbuf[1];//fu_hdr->E=0;fu_hdr->R=0;fu_hdr->S=0;fu_hdr->E=0;fu_hdr->TYPE=n->nal_unit_type;nalu_payload=&sendbuf[2];//同理将sendbuf[14]的地址赋给nalu_payloadmemcpy(nalu_payload,n->buf+t*MAX_RTP_PKT_LENGTH+1,MAX_RTP_PKT_LENGTH);//去掉起始前缀的nalu剩余内容写入sendbuf[14]开始的字符串。//status = session.SendPacket((void *)sendbuf,MAX_RTP_PKT_LENGTH+2);status = session.SendPacket((void *)sendbuf,MAX_RTP_PKT_LENGTH+2,96,false,0);if (status < 0){std::cerr << RTPGetErrorString(status) << std::endl;exit(-1);}t++;}}}#endif#if 0session.BeginDataAccess();if (session.GotoFirstSource()){do{RTPPacket *packet;while ((packet = session.GetNextPacket()) != 0){std::cout << "Got packet with "<< "extended sequence number "<< packet->GetExtendedSequenceNumber()<< " from SSRC " << packet->GetSSRC()<< std::endl;session.DeletePacket(packet);}} while (session.GotoNextSource());}session.EndDataAccess();
#endifRTPTime::Wait(delay);RTPTime t = RTPTime::CurrentTime();t -= starttime;if (t > RTPTime(60.0))break;}printf("over\n");delay = RTPTime(10.0);session.BYEDestroy(delay,"Time's up",9);//一些清理工作…
}

这些代码等所用到提到的全部东西都上传到了我的github中,大家可以到这找到我的完整的工程。

https://github.com/xyyangkun/test_jrtp.git

使用jrtplib打包发送h264数据,关使用vlc|mplayer播放相关推荐

  1. RTP打包发送H264(下)

    对于H264的I帧.P帧等主要是FU(分片)发送,那么FU到底是怎样一个过程呢. 相同NAL单元的分片必须使用递增的RTP序号连续顺序发送(第一和最后分片之间没有其他的RTP包).相似, NAL单元必 ...

  2. 【Android音视频开发】【015】通过MediaCodec和SurfaceView,对H264数据进行解帧和播放

    功能点 从连续的字节块中,解析分割出多个H264帧数据 通过MediaCodec解码H264帧 通过SurfaceView播放 代码 //H264数据解析package com.easing.comm ...

  3. Android之间互相的录屏直播 --点对点传输(tcp长连接发送h264)(一)

    前言 转载请注明出处 ,来自: 暂时两篇: (1) Android之间互相的录屏直播 –点对点传输(tcp长连接发送h264)(一) http://blog.csdn.net/baidu_335462 ...

  4. jrtplib开源库系列之三:jrtplib发送接收数据流程

    说明 前面2篇文章主要说明了如何安装jrtplib库,以及对example1进行了说明,这篇文章主要说下jrtplib库数据的收发流程. 数据收发流程 从例子1就可以很好的说明jrtplib的使用是非 ...

  5. 安防视频监控系统视频上云解决方案EasyCVR语音转发功能音频数据打包发送流程介绍

    目前我们的视频上云服务平台EasyCVR已经可集成海康EHome私有协议,并且在前文中我也跟大家讲过EHome协议的配置和调用流程,有兴趣的可以阅读一下:配置及协议介绍.Ehome协议调用流程介绍. ...

  6. c 使用RTP协议发送视频数据

    使用RTP协议发送视频数据,要先弄清楚RTP协议的格式,自己组包发送.下面具体说明了RTP固定头的内容和组包时的值. rtp包格式 V:2 P:0 X:0 CC:CSRC 计数,4位.表示跟在RTP固 ...

  7. 【Android RTMP】RTMPDump 推流过程 ( 独立线程推流 | 创建推流器 | 初始化操作 | 设置推流地址 | 启用写出 | 连接 RTMP 服务器 | 发送 RTMP 数据包 )

    文章目录 安卓直播推流专栏博客总结 一. Java 层传入的 RTMP 推流地址处理 二. RTMPDump 推流线程 三. 创建 RTMP 对象 四. 初始化 RTMP 对象 五. 设置 RTMP ...

  8. (转)rtmp协议简单解析以及用其发送h264的flv文件

    Adobe公司太坑人了,官方文档公布的信息根本就不全,如果只按照他上面的写的话,是没法用的.按照文档上面的流程,server和client连接之后首先要进行握手,握手成功之后进行一些交互,其实就是交互 ...

  9. 用socket发送流数据示--用 php://input? 接受post数据(可实现php和c/c++数据通讯)

    文件:phpinput_post.php 发送post数据(流数据),利用socket发送 <?php /** * 客服端发送post流数据的请求 */ //对数据进行封包 function p ...

最新文章

  1. Day 3: Flask —— 使用Python和OpenShift进行即时Web开发
  2. 敏捷开发knowledge
  3. 天才基本法_【书鱼扫文】天才基本法:天才与中国式青春
  4. jdk的ServiceLoader
  5. QT程序在windows下部署发布
  6. Atitit UEHP手机信息安全法 目录 1. 敏感数据清理 1 2. 数据集清理 1 3. Acc清理 1 4. 短信 通讯录 通话记录清理 1 5. Sim tf卡 2 6. 每个app过一
  7. java 游戏 异步框架_基于Java的轻量级异步编程框架
  8. 构建我的第一个 22TB 容量的家庭存储服务器
  9. Redis的性能瓶颈
  10. EF中一种简单的多条件动态查询方法
  11. python计算机视觉-图像处理基础章节第三章之根据仿射或单应性变换实现图像的扭曲,映射,融合
  12. ssrf漏洞修复(ssrf漏洞修复方式)
  13. PHP如何启动scrapy,python,_新手Scrapy爬虫运行问题,python - phpStudy
  14. JavaScript首次体验
  15. 软件分享之浏览器部分
  16. echart 环形饼图设置中心固定信息
  17. B站UP主恰饭新思路:产品糅合进有意思的内容里
  18. 入门前端框架Layui
  19. 仓储委外加工/周转加工
  20. “着色器”是什么意思? 如何使用HTML5和WebGL创建它们

热门文章

  1. em算法详细例子及推导_EM算法详解(例子+推导)
  2. php 客户端上传图片,php上传图片客户端和服务器端实现方法
  3. 4g网络什么时候淘汰_5G时代,4G将淘汰?4G手机会不会像2g,突然失去网络
  4. cpp 一个文件分成两个文件写_为ORB-SLAM2写一个launch文件
  5. net4.0 程序没反应_@Java程序员,精通Spring,你不得不知道的那些书
  6. java高并发(五)并发模拟
  7. Pandownload惊喜复活?这一次请你低调使用!
  8. 关于毕业租房的一些碎碎念。
  9. 学习难?求职难?90分钟点亮你的AI求职之路!
  10. GitHub 发布重磅更新:你电脑上的 IDE 可以删了?!