技术标签: live555
1、testRTSPClient简介
testRTSPClient是个简单的客户端实例,这个实例对rtsp数据交互作了详细的描述,其中涉及到rtsp会话的两个概念Source和Sink.
Source是生产数据,Sink是消费数据.
testRTSPClient非常简洁,除了接收服务端发送过来的数据,什么都没干,所以我们很方便在这个基础上改造,做我们自己的项目.
2、testRTSPClient编译,运行
在linux下编译运行更方便,鉴于我的电脑太渣,虚拟机跑起来费劲,就转到windows下来折腾.
在windows下只需要加载这一个文件就可以编译,我们以mediaServer为服务端,以testRTSPClient为客户端。
当然也可以用支持rtsp协议的摄像机或其他实体设备作为服务端。
先启动mediaServer,然后在testRTSPClient项目的命令菜单里填入mediaServer 提示的IP, 再启动testRTSPClient即可。
3、testRTSPClient核心代码解读
1)看代码之前可以大致浏览一下总体的框架,这位博主画了个流程图http://blog.csdn.net/smilestone_322/article/details/17297817
复制代码
void DummySink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes,
struct timeval presentationTime, unsigned /durationInMicroseconds/) {
// We’ve just received a frame of data. (Optionally) print out information about it:
#ifdef DEBUG_PRINT_EACH_RECEIVED_FRAME
if (fStreamId != NULL) envir() << "Stream “” << fStreamId << “”; “;
envir() << fSubsession.mediumName() << “/” << fSubsession.codecName() << “:\tReceived " << frameSize << " bytes”;
if (numTruncatedBytes > 0) envir() << " (with " << numTruncatedBytes << " bytes truncated)”;
char uSecsStr[6+1]; // used to output the ‘microseconds’ part of the presentation time
sprintf(uSecsStr, “%06u”, (unsigned)presentationTime.tv_usec);
envir() << ".\tPresentation time: " << (unsigned)presentationTime.tv_sec << “.” << uSecsStr;
if (fSubsession.rtpSource() != NULL && !fSubsession.rtpSource()->hasBeenSynchronizedUsingRTCP()) {
envir() << “!”; // mark the debugging output to indicate that this presentation time is not RTCP-synchronized
}
envir() << “\n”;
#endif
// Then continue, to request the next frame of data:
continuePlaying();
}
Boolean DummySink::continuePlaying() {
if (fSource == NULL) return False; // sanity check (should not happen)
// Request the next frame of data from our input source. “afterGettingFrame()” will get called later, when it arrives:
fSource->getNextFrame(fReceiveBuffer, DUMMY_SINK_RECEIVE_BUFFER_SIZE,
afterGettingFrame, this,
onSourceClosure, this);
return True;
}
复制代码
2)有网友在testRTSPClient基础上,把接收的数据写成h264文件了http://blog.csdn.net/occupy8/article/details/36426821
复制代码
void DummySink::afterGettingFrame(void* clientData, unsigned frameSize, unsigned numTruncatedBytes,
struct timeval presentationTime, unsigned durationInMicroseconds) {
DummySink* sink = (DummySink*)clientData;
sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime, durationInMicroseconds);
}
// If you don’t want to see debugging output for each received frame, then comment out the following line:
#define DEBUG_PRINT_EACH_RECEIVED_FRAME 1
void DummySink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes,
struct timeval presentationTime, unsigned /durationInMicroseconds/) {
// We’ve just received a frame of data. (Optionally) print out information about it:
#ifdef DEBUG_PRINT_EACH_RECEIVED_FRAME
if (fStreamId != NULL) envir() << "Stream “” << fStreamId << “”; “;
envir() << fSubsession.mediumName() << “/” << fSubsession.codecName() << “:\tReceived " << frameSize << " bytes”;
if (numTruncatedBytes > 0) envir() << " (with " << numTruncatedBytes << " bytes truncated)”;
char uSecsStr[6+1]; // used to output the ‘microseconds’ part of the presentation time
sprintf(uSecsStr, “%06u”, (unsigned)presentationTime.tv_usec);
envir() << ".\tPresentation time: " << (unsigned)presentationTime.tv_sec << “.” << uSecsStr;
if (fSubsession.rtpSource() != NULL && !fSubsession.rtpSource()->hasBeenSynchronizedUsingRTCP()) {
envir() << “!”; // mark the debugging output to indicate that this presentation time is not RTCP-synchronized
}
envir() << “\n”;
#endif
//todo one frame
//save to file
if(!strcmp(fSubsession.mediumName(), “video”))
{
if(firstFrame)
{
unsigned int num;
SPropRecord *sps = parseSPropParameterSets(fSubsession.fmtp_spropparametersets(), num);
// For H.264 video stream, we use a special sink that insert start_codes:
struct timeval tv= {0,0};
unsigned char start_code[4] = {0x00, 0x00, 0x00, 0x01};
FILE *fp = fopen(“test.264”, “a+b”);
if(fp)
{
fwrite(start_code, 4, 1, fp);
fwrite(sps[0].sPropBytes, sps[0].sPropLength, 1, fp);
fwrite(start_code, 4, 1, fp);
fwrite(sps[1].sPropBytes, sps[1].sPropLength, 1, fp);
fclose(fp);
fp = NULL;
}
delete [] sps;
firstFrame = False;
}
char *pbuf = (char *)fReceiveBuffer;
char head[4] = {0x00, 0x00, 0x00, 0x01};
FILE *fp = fopen("test.264", "a+b");
if(fp)
{
fwrite(head, 4, 1, fp);
fwrite(fReceiveBuffer, frameSize, 1, fp);
fclose(fp);
fp = NULL;
}
}
// Then continue, to request the next frame of data:
continuePlaying();
}
Boolean DummySink::continuePlaying() {
if (fSource == NULL) return False; // sanity check (should not happen)
// Request the next frame of data from our input source. “afterGettingFrame()” will get called later, when it arrives:
fSource->getNextFrame(fReceiveBuffer, DUMMY_SINK_RECEIVE_BUFFER_SIZE,
afterGettingFrame, this,
onSourceClosure, this);
return True;
}
复制代码
testRTSPClient接收的fReceiveBuffer缓存没有起始码,start_code[4] = {0x00, 0x00, 0x00, 0x01}; 写成文件或者播放都需要自行加上。
3)testRTSPClient这个实例还支持多路录放,网上搜到有人已经实现了,搬过来.
http://blog.chinaunix.net/uid-15063109-id-4482932.html
最近经常思考这个问题,如何评价B端产品经理的能力。根据我反复的揣测,大概能归类为以下四个方面。1.能不能用生意的视角看业务好的生意的最大特点是2个原则,其一是高利润,其二是可持续。B端产品经理可能会面临很多业务线,是否所有业务都满足有利润、可持续这两个原则,需要产品经理去甄选。所有业务都接,并且把业务做好的产品经理不一定是个称职的产品。核心要看能不能从生意的视角出发去对待业务,有取有舍,追大放小,有独到的战略眼光在商业上破局才是个合格的B端产品经理。2.能不能用业务的视角看...
爬取思路:前两天有网友让我帮忙做一个通过快递100这个网站查快递物流信息的爬虫。研究了一下,发现实现起来不是很复杂,在此整理成文档并share给大家。爬取思路:输入运单号后从服务器获取快递公司对应的ID号,然后拼接出新的请求地址来获取相关信息。代码分解:class KuaiDi100(): def __init__(self): self.comCode = "" self.temp = "" self.url = r"https://
写在前面这段时间一直在学习 Flutter,在 dribble 上看到一张导航栏设计图,就是下面这张,感觉很是喜欢,于是思考着如何在 Flutter 中实现这个效果。设计图作者:Lukáš Straňák经过一番研究,大体上算是实现了效果(有些地方还是需要改进的),如下:这篇文章和大家分享一下实现过程,一起交流、学习。重点阅读实现这个效果主要用到了 AnimationControl...
STM32F407ZGT6最小系统原理图和PCB
原文链接:https://www.lianxh.cn/news/32ae13ec789a1.html目录1. 什么是稳健性检验? 2. 为什么要做稳健性检验? 3. 变量替换法 3.1 替换因变量 3.2 替换自变量 3.3 放宽因变量或自变量条件 4. 补充变量法 4.1 加入遗漏变量 4.2 加入各类虚拟变量 5. 分样本回归法 6. 调整样本期 6.1 扩展时间窗口 6.2 缩短时间窗口 6.3 滚动窗口法 7. 改变样本容量法 7.
分布式集群环境下使用redis保证只有一个定时任务在执行
一.基本流程:获取图像---矫正图像---确定ROI---提取边缘或直线---计算轮廓属性---处理多边形轮廓---转换到世界坐标系---结果可视化。二.常用算子:利用形态学算子计算区域边缘并返回。BoundaryType:'inner', 'outer', 'inner_filled'原始区域内部;外部;内部填充,边缘皆是一个像素宽。返回区域的并集。利用Deriche, Lanser, Shen...
part1 了解数据集基本情况当我们面对一个很大的数据集的时候,如何快速得到他的行数与列数呢?举个栗子,我们读取一个nhanes数据集,我们加上一个.shape,输出的结果就是他的行数和列数了。也就是:(jupyter笔记本形式)当我们想看他的列名分别有什么时,我们加上一个.columns也就是:如果想知道表格各列的数据类型:也就是:part2 提取某一列在这个程序里,w , x , y , z ...
第二阶段部署UBOOT,uImage,rootfs到TF卡中准备一张TF卡,最好是金士顿的TF卡,我一开始用的不是金士顿的卡,总是部署失败,后来我换了一张32G的金士顿TF卡,成功部署!像一般的金士顿1G,2G卡也没应该有问题。1.2.1烧写UBOOT到TF卡插入TF卡,ubuntu12.04会识别。在驱动文件下可以看到/dev/sdc,文件。有些会识别成/dev/sdb,根据自己的系
背景描述: 今天在看redis的内容,看到同事在配置文件中将bind配置为0.0.0.0进行监听,不明白什么意思就查询了下,在此记录下。解释:0.0.0.0在服务器的环境中,指的就是服务器上所有的ipv4地址,如果机器上有2个ip 192.168.30.10 和 10.0.2.15,redis在配置中,如果配置监听在0.0.0.0这个地址上,那么,通过这2个ip地址都是能够到达这个r...
问题来源于 ComBox.DroppedDown := True;这样可以弹出下拉框,但鼠标指针会消失,尝试使用ShowCursor(False); //隐藏光标ShowCursor(True); //显示光标SendMessage(ComBox.Handle, WM_SETCURSOR,0,0); //这样可以显示出光标反复测试ShowCursor,成对调用可以隐藏和显示,可ShowCursor...
提供解决思路,此文实为借鉴。前言:利用selenium模拟登录淘宝,外界传言这个确实很难过,有各种轨迹检测。但是我没想到淘宝的滑块验证码这么容易就能过。私信小编001即可获取大量Python学习资料。开发工具**Python版本:**3.6.4相关模块:selenium模块;python内置模块。Chromedriver:自行谷歌,下载和电脑上的谷歌浏览器版本相匹配的驱动。selenium模块本质是通过驱动浏览器,完全模拟浏览器的操作,比如跳转、输入、点击、下拉等,来拿到网页渲