这篇记录我在2015年的时候写的一个从远程摄像头服务器读取视频流的工作。先介绍RTSP通信部分。
RTSP协议
RTSP(Real Time Streaming Protocol)是由Real Network和Netscape共同提出的如何有效地在IP网络上传输流媒体数据的应用层协议。RTSP对流媒体提供了诸如暂停,快进等控制,而它本身并不传输数据,RTSP的作用相当于流媒体服务器的远程控制。服务器端可以自行选择使用TCP或UDP来传送串流内容,它的语法和运作跟HTTP 1.1类似,但并不特别强调时间同步,所以比较能容忍网络延迟。
参考资料:
http://blog.csdn.net/lory17/article/details/60144734
libcurl
libcurl主要功能就是用不同的协议连接和沟通不同的服务器~也就是相当封装了的sockPHP 支持libcurl(允许你用不同的协议连接和沟通不同的服务器)。, libcurl当前支持http, https, ftp, gopher, telnet, dict, file, 和ldap 协议。libcurl同样支持HTTPS证书授权,HTTP POST, HTTP PUT, FTP 上传(当然你也可以使用PHP的ftp扩展), HTTP基本表单上传,代理,cookies,和用户认证。
https://baike.baidu.com/item/libcurl/5256898?fr=aladdin
基于libcurl实现RTSP
#pragma once
#include <string>
struct curl_slist;
typedef void CURL;
class RTSPClient
{
public:
RTSPClient(std::string url);
~RTSPClient();
void options(const char *uri);
std::string describe(const char *uri);
void setup(const char *uri,const char *transport);
void play(const char *uri,const char *range);
void teardown(const char *uri);
private:
CURL *mCurl = NULL;
std::string mUrl;
};
//回调函数
static size_t onWriteData(void *buffer, size_t size, size_t nmemb, void *lpvoid);
static size_t onHeader(char *buffer, size_t size, size_t nitems, void *userdata);
需要实现几个基本的操作,比如:setup,play,teardown等。回调函数定义成静态函数。
#include "RTSPClient.h"
#include <curl/curl.h>
#include <string>
using std::string;
RTSPClient::RTSPClient(std::string url)
{
CURLcode res;
//In windows, this will init the winsock stuff
res = curl_global_init(CURL_GLOBAL_ALL);
if (res == CURLE_OK) {
curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
fprintf(stderr, " cURL V%s loaded\n", data->version);
/* get a curl handle */
mCurl = curl_easy_init();
if (mCurl != NULL) {
curl_easy_setopt(mCurl, CURLOPT_URL, url.c_str());
curl_easy_setopt(mCurl, CURLOPT_PROTOCOLS, (long)CURLPROTO_RTSP);
mUrl = url;
}
else {
fprintf(stderr, "curl_easy_init() failed\n");
}
}
else {
fprintf(stderr, "curl_global_init(%s) failed: %d\n",
"CURL_GLOBAL_ALL", res);
}
}
RTSPClient::~RTSPClient()
{
curl_global_cleanup();
}
void RTSPClient::options(const char *uri)
{
string responseHeader;
curl_easy_setopt(mCurl, CURLOPT_RTSP_STREAM_URI, uri);
curl_easy_setopt(mCurl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_OPTIONS);
//应答消息头部
curl_easy_setopt(mCurl, CURLOPT_HEADERFUNCTION, onHeader);
//打印到字符串
curl_easy_setopt(mCurl, CURLOPT_HEADERDATA, (void *)&responseHeader);
CURLcode res=curl_easy_perform(mCurl);
}
string RTSPClient::describe(const char *uri)
{
string responseStr;
string responseHeader;
curl_easy_setopt(mCurl, CURLOPT_RTSP_STREAM_URI, uri);
curl_easy_setopt(mCurl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_DESCRIBE);
//应答消息主体
curl_easy_setopt(mCurl, CURLOPT_WRITEFUNCTION, onWriteData);
//打印到字符串
curl_easy_setopt(mCurl, CURLOPT_WRITEDATA, (void *)&responseStr);
//应答消息头部
curl_easy_setopt(mCurl, CURLOPT_HEADERFUNCTION, onHeader);
//打印到字符串
curl_easy_setopt(mCurl, CURLOPT_HEADERDATA, (void *)&responseHeader);
curl_easy_perform(mCurl);
return responseStr;
}
void RTSPClient::setup(const char *uri,const char *transport)
{
string responseHeader;
curl_easy_setopt(mCurl, CURLOPT_RTSP_STREAM_URI, uri);
curl_easy_setopt(mCurl, CURLOPT_RTSP_TRANSPORT, transport);
curl_easy_setopt(mCurl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_SETUP);
//应答消息头部
curl_easy_setopt(mCurl, CURLOPT_HEADERFUNCTION, onHeader);
//打印到字符串
curl_easy_setopt(mCurl, CURLOPT_HEADERDATA, (void *)&responseHeader);
curl_easy_perform(mCurl);
}
void RTSPClient::play(const char *uri,const char *range)
{
string responseHeader;
curl_easy_setopt(mCurl, CURLOPT_RTSP_STREAM_URI, uri);
curl_easy_setopt(mCurl, CURLOPT_RANGE, range);
curl_easy_setopt(mCurl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_PLAY);
//应答消息头部
curl_easy_setopt(mCurl, CURLOPT_HEADERFUNCTION, onHeader);
//打印到字符串
curl_easy_setopt(mCurl, CURLOPT_HEADERDATA, (void *)&responseHeader);
curl_easy_perform(mCurl);
}
void RTSPClient::teardown(const char * uri)
{
string responseHeader;
curl_easy_setopt(mCurl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_TEARDOWN);
//应答消息头部
curl_easy_setopt(mCurl, CURLOPT_HEADERFUNCTION, onHeader);
//打印到字符串
curl_easy_setopt(mCurl, CURLOPT_HEADERDATA, (void *)&responseHeader);
curl_easy_perform(mCurl);
}
static size_t onWriteData(void *buffer, size_t size, size_t nmemb, void *lpvoid) {
string *str = dynamic_cast<string *>((string *)lpvoid);
if (str == NULL || buffer == NULL) {
return -1;
}
char *data = (char *)buffer;
str->append(data, size*nmemb);
return nmemb;
}
static size_t onHeader(char *buffer, size_t size,
size_t nitems, void *userdata)
{
/* received header is nitems * size long in 'buffer' NOT ZERO TERMINATED */
/* 'userdata' is set with CURLOPT_HEADERDATA */
string *str = (string *)userdata;
if (str == NULL || buffer == NULL) {
return -1;
}
str->append(buffer, size*nitems);
return nitems * size;
}
完成!