前言:学习一个协议总是枯燥乏味,如果能快速做出个小成品来,然后根据协议不断完善其功能,那就好多了。下面分享一个rtsp服务端的示例小程序,代码200多行,VLC和ffmpeg能正常拉多路流,适合新手探究rtsp协议,做rtsp服务器。

工程压缩包:下载链接(csdn: 简单rtsp服务端实现, 百度云盘:链接: https://pan.baidu.com/s/18BsIqEcvGY9cXw25iyub0A 提取码: 72hi ),包含以下代码,以及编译好的用到的media-server头文件和库文件,以及一段h264录像(做视频源)。

源码: 服务基于开源的media-server库在windows上实现,用来封装rtsp码流的数据包,信令部分直接固定字符串返回,配合wireshark抓包学rtsp协议很方便。

media-server源码链接:https://github.com/ireader/media-server

编译media-server需要用到sdk库,源码链接:https://github.com/ireader/sdk

主函数源码:

#include <iostream>
#include <WinSock2.h>
#include "rtspsession.h"#pragma comment(lib, "winmm.lib")
#pragma comment(lib, "Iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "librtp.lib")SOCKET Listener = 0;
std::vector<RtspSessionPtr> AllRtspSession;int ListenLocalSvc(int port);int main()
{std::cout << "hello rtsp server!" << std::endl;if (0 > ListenLocalSvc(554) ||0 > RtspSession::ReadSourceStream("miniH264.h264")){return -1;}unsigned int startTime, usedTime;while (true){startTime = GetTickCount();struct sockaddr_in clent;int len = sizeof(clent);int clientSocket = accept(Listener, (sockaddr*)&clent, &len);if (clientSocket > 0) {printf("accept a client %d\n", clientSocket);int value = 10 * 1024 * 1024;setsockopt(clientSocket, SOL_SOCKET, SO_SNDBUF, (char*)&value, sizeof(value));AllRtspSession.push_back(std::make_shared<RtspSession>(clientSocket));}for (auto it = AllRtspSession.begin(); it != AllRtspSession.end(); ){it = (0 > (*it)->Run()) ? AllRtspSession.erase(it) : ++it;}usedTime = GetTickCount() - startTime;if (usedTime < 5)Sleep(5 - usedTime);}return 0;
}int ListenLocalSvc(int port)
{WSADATA WSAData;WSAStartup(MAKEWORD(2, 0), &WSAData);Listener = socket(AF_INET, SOCK_STREAM, 0);unsigned long ul = 1;int ret = ioctlsocket(Listener, FIONBIO, (unsigned long *)&ul);//设置成非阻塞模式。 struct sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_addr.s_addr = 0;sin.sin_port = htons(port);if (bind(Listener, (struct sockaddr*) &sin, sizeof(sin)) < 0) {printf("bind port %d failed", port);closesocket(Listener);return -1;}if (listen(Listener, 20) < 0) {printf("listen port %d failed", port);closesocket(Listener);return -1;}return 0;
}

RtspSession类头文件:

#pragma once
#include <memory>
#include <vector>enum RS_Status
{RS_INIT,RS_SIGNALLING,RS_PAUSE,RS_SENDSTREAM,RS_ERROR
};class RtspSession
{unsigned int mFrameSeq;unsigned long mLastFrameTS;void *mPacker;int SendStream();int RecvSignaling();
public:int mSocket;RS_Status mStatus;RtspSession(int sock);~RtspSession();static int ReadSourceStream(char *fileName);int Run();
};using RtspSessionPtr = std::shared_ptr<RtspSession>;

RtspSession类源文件:

#include "rtspsession.h"
#include <WinSock2.h>
#include <string>
#include "rtp-payload.h"char buffer[5 * 1024];
char rtpPkg[1600];
std::vector<char*> StreamFrame;std::string GetMember(const char* source, const char* memberName)
{const char * pStart = strstr(source, memberName);if (pStart != NULL){const char * pEnd = strchr(pStart, '\n');if (pEnd != NULL){if (*(pEnd - 1) == '\r')pEnd--;return std::string(pStart, pEnd);}}return "";
}void* RTPAlloc(void* param, int bytes)
{return rtpPkg + 4;
}void RTPFree(void* param, void *packet)
{
}int RTPPacket(void* param, const void *packet, int bytes, uint32_t, int)
{rtpPkg[0] = '$';rtpPkg[1] = 0;*(short*)(rtpPkg + 2) = htons(bytes);if (0 > send(((RtspSession*)param)->mSocket, rtpPkg, bytes + 4, 0)){((RtspSession*)param)->mStatus = RS_ERROR;}return 0;
}struct rtp_payload_t rtpfunc = { RTPAlloc, RTPFree, RTPPacket, };RtspSession::RtspSession(int sock):mSocket(sock),mFrameSeq(0),mPacker(NULL),mLastFrameTS(0),mStatus(RS_INIT)
{
}RtspSession::~RtspSession()
{if(mPacker)rtp_payload_encode_destroy(mPacker);closesocket(mSocket);printf("close socket %d\n", mSocket);
}int RtspSession::ReadSourceStream(char *fileName)
{FILE *fp = fopen(fileName, "rb");if (fp == NULL){printf("open file:%s error\n", fileName);return -1;}unsigned int size;while (true){if (4 != fread(&size, 1, 4, fp))break;char *frame = (char*)malloc(size + 5);memcpy(frame, &size, 4);if (size != fread(frame + 4, 1, size, fp))break;;StreamFrame.push_back(frame);}fclose(fp);return 0;
}int RtspSession::Run()
{if (mStatus == RS_ERROR){return -1;}else if (mStatus == RS_SENDSTREAM){SendStream();}return RecvSignaling();
}int RtspSession::RecvSignaling()
{int len = recv(mSocket, buffer, 5 * 1024, 0);if (len <= 0){return 0;}buffer[len] = 0;printf("recv:%s\n", buffer);std::string rsp;if (std::string(buffer, 7) == "OPTIONS"){rsp = "RTSP/1.0 200 OK\r\n"\"CSeq: 0\r\n"\"Date: Tue, 11 Nov 2020 11:11:11 GMT\r\n"\"Public: DESCRIBE,SETUP,TEARDOWN,PLAY,PAUSE,ANNOUNCE,RECORD,GET_PARAMETER,SET_PARAMETER\r\n"\"Content-Length: 0\r\n\r\n";}else if (std::string(buffer, 8) == "DESCRIBE"){rsp = "RTSP/1.0 200 OK\r\n"\"CSeq: 0\r\n"\"Date: Tue, 11 Nov 2020 11:11:11 GMT\r\n"\"Content-Type: application/sdp\r\n"\"Content-Length: 270\r\n\r\n";std::string sdp = "v=0\n"\"o=- 0 0 IN IP4 0.0.0.0\n"\"s=rtsp://127.0.0.0:554/virtual_channel\n"\"c=IN IP4 0.0.0.0\n"\"t=0 0\n"\"a=range:npt=now-\n"\"a=recvonly\n"\"a=control:*\n"\"m=video 0 RTP/AVP 97\n"\"a=rtpmap:97 H264/90000\n"\"a=fmtp:97\n"\"a=control:track0\n";rsp.replace(rsp.find("Content-Length: 270") + 16, 3, std::to_string(sdp.size()));rsp += sdp;}else if (strstr(buffer, "RTP/AVP")){rsp = "RTSP/1.0 461 Unsupported Transport\r\n"\"CSeq: 0\r\n"\"Date: Tue, 11 Nov 2020 11:11:11 GMT\r\n"\"Transport: \r\n"\"Content-Length: 0\r\n\r\n";if (strstr(buffer, "RTP/AVP/TCP")){rsp = "RTSP/1.0 200 OK\r\n"\"CSeq: 0\r\n"\"Date: Tue, 11 Nov 2020 11:11:11 GMT\r\n"\"Session: 0x7fc1b8005d88\r\n"\"Transport: RTP/AVP/TCP;interleaved=0-1\r\n"\"Content-Length: 0\r\n\r\n";}}else if (std::string(buffer, 4) == "PLAY"){rsp = "RTSP/1.0 200 OK\r\n"\"CSeq: 0\r\n"\"Date: Tue, 11 Nov 2020 11:11:11 GMT\r\n"\"Session: 0x7fc1b8005d88\r\n"\"Range: npt=0.000- \r\n"\"RTP-Info: url=rtsp://127.0.0.0:554/virtual_channel;seq=0;rtptime=0\r\n"\"Content-Length: 0\r\n\r\n";mStatus = RS_SENDSTREAM;}printf("send:%s\n", rsp.c_str());if (!rsp.empty()){std::string seq = GetMember(buffer, "CSeq");if (!seq.empty()){rsp.replace(rsp.find("CSeq: 0"), 7, seq.c_str());}return send(mSocket, rsp.c_str(), rsp.size(), 0);}return 0;
}int RtspSession::SendStream()
{if(mPacker == NULL)mPacker = rtp_payload_encode_create(97, "H264", 0, 0xffff, &rtpfunc, this);if (GetTickCount() - mLastFrameTS > 40){mLastFrameTS = GetTickCount();char *frame = StreamFrame[mFrameSeq%StreamFrame.size()];rtp_payload_encode_input(mPacker, frame + 4, *(int*)frame, mFrameSeq * 3600);mFrameSeq++;}return 0;
}

基于media-server简单的rtsp服务端实现相关推荐

  1. [2018 CS:GO Server]2018 Old CSGO 服务端,回忆逝去的青春!

    简介: 2018 CSGO作为最后一代老UI,成为了许多老玩家逝去的青春,在这一年CSGO正式更新了全景UI等其他地方,本文章提供CSGO 2018服务端下载以及搭建教程,纯原创,欢迎各位的支持! 鸣 ...

  2. 基于TCP/IP协议的Java服务端与Android客户端的Socket通信及数据交互

    基于TCP/IP协议的Java服务端与Android客户端的Socket通信及数据交互 一.前言 1.Java服务端程序代码的项目名为TcpSocketServerOfJava,包名为com.exam ...

  3. 基于 azerothcore-wotlk 构建docker wow 335服务端

    介绍 基于 azerothcore-wotlk 构建docker wow 335服务端 软件架构 Ubuntu 20.04 安装教程 1.下载 docker 环境 更换国内apt源,自行选择 清华源 ...

  4. 基于Flask+Nginx+uWSGI实现CentOS服务端模型部署及预加载

    基于Flask+Nginx+uWSGI实现CentOS服务端模型部署及预加载http://www.manongjc.com/article/37802.html

  5. socket java 客户端_Java基于socket实现的客户端和服务端通信功能完整实例

    本文实例讲述了Java基于socket实现的客户端和服务端通信功能.分享给大家供大家参考,具体如下: 以下代码参考马士兵的聊天项目,先运行ChatServer.java实现端口监听,然后再运行Chat ...

  6. python实现淘宝客服自动回复语_Python+Socket实现基于TCP协议的客户与服务端中文自动回复聊天功能示例...

    本文实例讲述了Python+Socket实现基于TCP协议的客户与服务端中文自动回复聊天功能.分享给大家供大家参考,具体如下: [吐槽] 网上的代码害死人,看着都写的言之凿凿,可运行就是有问题. 有些 ...

  7. C++RTSP服务端(附源码)

      VC++开发常用功能一系列文章 (欢迎订阅,持续更新...) 第16章:VC++ RTSP服务端(附源码)  源代码demo已上传到百度网盘:永久生效 ,文章尾部附 百度链接

  8. 如果input标签中有runat=”server”,则在服务端,Request.Form接收不到

    如果input标签中有runat="server",则在服务端,Request.Form接收不到 转载于:https://www.cnblogs.com/street-boy/p/ ...

  9. 【学习笔记】在windows下进行基于TCP的本地客户端和服务端socket通信

    文章目录 socket介绍 java中使用socket 基于tcp的socket通信 使用ServerSocket类创建一个web服务器:(java) windows下的基于tcp的socket编程( ...

最新文章

  1. Linux内核文件vmlinux 和压缩后的bzImage文件格式分析
  2. 职业高中计算机原理,132-浅议职业高中计算机组成原理教法初探
  3. (转)所有iOS设备的屏幕分辨率
  4. ONLY三行脚本, SQL数据恢复到指定时间点
  5. linux cp通同时新建目录_Linux 新手应该知道的 26 个命令
  6. ora 27102 linux,ORA-27102: out of memory Linux-x86_64 Error: 12: Cannot allocate memory
  7. 跨网页的新手引导_做自媒体的新手要注意什么,这些坑不能踩,这些事不能做...
  8. setValuesForKeysWithDictionary:的用途
  9. NLP - 结巴分词 词云
  10. 百度离线地图JS API V3.0
  11. 微信小游戏代码包侵权的一种解决方案
  12. react native 随手记之打包遇到坑
  13. 幼儿园故事导入语案例_幼儿园讲故事的教案10篇
  14. php中mysql_assoc,在PHP中使用mysql_fetch_assoc时出现警告
  15. Android Studio | 页面布局
  16. 服务器生成微信sign,签名生成方法
  17. 软通动力上市难掩隐忧,软件外包模式或受冲击
  18. 基于隐马尔科夫模型文本相似度问题研究
  19. 美团三面:一直追问我, MySQL 幻读被彻底解决了吗?
  20. 关于java集合的练习

热门文章

  1. Pr菜鸟入门教程(背景音乐和字幕部分)
  2. cnpm不是内部命令的问题
  3. Raptor-回文数判断
  4. Numpy数组中删除指定位置、指定行或指定列的数据:numpy.delete()
  5. mysql锁表的解决
  6. fgo服务器维护检测脚本,fgo脚本会封号吗过验证用
  7. 案例35:基于Springboot图书商城管理系统开题报告设计
  8. Windows 环境下安装 Yapi 教程
  9. python输入一个正整数,输出它的因数
  10. 网络营销的六大特点和八大功能