Всем привет, мы снова встретились, я ваш друг Цюаньчжаньцзюнь.
RTSP (протокол потоковой передачи в реальном времени) — это протокол прикладного уровня, совместно предложенный Real Network и Netscape для эффективной передачи потокового мультимедиа в IP-сетях. RTSP обеспечивает такие элементы управления, как приостановка и быстрая пересылка потокового мультимедиа, но сам он не передает данные. Роль RTSP эквивалентна удаленному управлению сервером потокового мультимедиа. Сервер может использовать TCP или UDP для передачи потокового контента. Его синтаксис и работа аналогичны HTTP1.1, но он не уделяет особого внимания синхронизации времени, поэтому он более терпим к сетевым задержкам.
RTP основан на протоколе UDP. UDP не требует установления соединения и является более эффективным. Но допускается потеря пакетов, что требует немного больше работы при повторной сборке носителя. RTP только упаковывает информацию о содержимом, тогда как RTCP обменивается управляющей информацией, а Qos реализуется через RTCP.
Приложение соответствует таким командам, как воспроизведение, поиск, пауза, остановка и т. д. RTSP обрабатывает эти команды и использует RTP (RTCP) во время передачи UDP. Если это TCP-соединение, RTP (RTCP) использоваться не будет.
Сервер подключения клиента RTSP передается через SDP (протокол описания сеанса).
Существует два основных типа сообщений RTSP: одно — сообщение запроса (запрос), а другое — ответное сообщение (ответ). Форматы этих двух сообщений различны.
метод URI RTSP-версия CR LF
заголовок сообщения CR LF CR LF
тело сообщения CR LF
Методы включают: ОПЦИИ, НАСТРОЙКА, ВОСПРОИЗВЕДЕНИЕ, РАЗБОР, ОПИСАНИЕ.
URI — адрес получателя (сервера), например: rtsp://192.168.6.136:5000/v0
CR LF после каждой строки означает возврат каретки и перевод строки, что требует соответствующего анализа на принимающей стороне. Заголовок сообщения должен иметь два CR LF.
DESCRIBE rtsp://192.168.1.211 RTSP/1.0
CSeq: 1
Accept: application/sdp
User-Agent: magnus-fc
RTSP-версия код состояния объяснять CR LF
заголовок сообщения CR LF CR LF
тело сообщения CR LF
Версия RTSP обычно RTSP/1.0, код состояния представляет собой числовое значение, 200 означает успех, а объяснение представляет собой текстовое объяснение, соответствующее коду состояния. Подробную информацию см. Введение в протокол SDP.
RTSP/1.0 200 OK
CSeq: 1
Server: GrandStream Rtsp Server V100R001
Content-Type: application/sdp
Content-length: 256
Content-Base: rtsp://192.168.1.211/0
v=0
o=StreamingServer 3331435948 1116907222000 IN IP4 192.168.1.211
s=h264.mp4
c=IN IP4 0.0.0.0
t=0 0
a=control:*
m=video 0 RTP/AVP 96
a=control:trackID=0
a=rtpmap:96 H264/90000
m=audio 0 RTP/AVP 97
a=control:trackID=1
a=rtpmap:97 G726-16/8000
C означает rtsp-клиент, S означает rtsp-сервер.
step1:
C->S:OPTION request //Спрашиваем S, какие методы доступны
S->C:OPTION response //S回应信息中包括提供из所有可用метод
step2:
C->S:DESCRIBE request //Требуем информацию описания инициализации носителя, предоставленную S
S->C:DESCRIBE response //S отвечает на информацию описания инициализации носителя, в основном sdp
step3:
C->S:SETUP request //Устанавливаем атрибуты сеанса и режим передачи, чтобы напомнить S о необходимости установить сеанс
S->C:SETUP response //S устанавливаем сеанс,Возвращает идентификатор сеанса,и информация, связанная с сеансом
step4:
C->S:PLAY request //C запрашивает воспроизведение
S->C:PLAY response //S отвечаем на запрос информации
S->C: //Отправляем потоковые данные
step5:
C->S:TEARDOWN request //C запрос на закрытие сессии
S->C:TEARDOWN response //S отвечает на запрос
Процесс преобразования статуса команды выглядит следующим образом:
Описание метода:
Получить доступные методы, предоставляемые сервером
OPTIONS rtsp://192.168.20.136:5000/xxx666 RTSP/1.0
CSeq: 1 //Каждое сообщение помечается порядковым номером. Первый пакет обычно представляет собой сообщение с запросом опции.
User-Agent: VLC media player (LIVE555 Streaming Media v2005.11.10)
Информация об ответе сервера включает в себя некоторые из предоставленных методов, например:
RTSP/1.0 200 OK
Server: UServer 0.9.7_rc1
Cseq: 1 //Значение cseq каждого ответного сообщения соответствует cseq сообщения запроса
Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, SCALE,GET_PARAMETER //服务器提供из可用изметод
C инициирует запрос DESCRIBE к S, чтобы получить информацию описания сеанса (SDP):
DESCRIBE rtsp://192.168.20.136:5000/xxx666 RTSP/1.0
CSeq: 2
token:
Accept: application/sdp
User-Agent: VLC media player (LIVE555 Streaming Media v2005.11.10)
Сервер отвечает некоторой описательной информацией (sdp) для этого сеанса:
RTSP/1.0 200 OK
Server: UServer 0.9.7_rc1
Cseq: 2
x-prev-url: rtsp://192.168.20.136:5000
x-next-url: rtsp://192.168.20.136:5000
x-Accept-Retransmit: our-retransmit
x-Accept-Dynamic-Rate: 1
Cache-Control: must-revalidate
Last-Modified: Fri, 10 Nov 2006 12:34:38 GMT
Date: Fri, 10 Nov 2006 12:34:38 GMT
Expires: Fri, 10 Nov 2006 12:34:38 GMT
Content-Base: rtsp://192.168.20.136:5000/xxx666/
Content-Length: 344
Content-Type: application/sdp
v=0 //Далее представлена информация SDP
o=OnewaveUServerNG 1451516402 1025358037 IN IP4 192.168.20.136
s=/xxx666
u=http:///
e=admin@
c=IN IP4 0.0.0.0
t=0 0
a=isma-compliance:1,1.0,1
a=range:npt=0-
m=video 0 RTP/AVP 96 //m представляет описание мультимедиа. Ниже приводится описание мультимедиа видеоканала в сеансе.
a=rtpmap:96 MP4V-ES/90000
a=fmtp:96 profile-level-id=245;config=000001B0F5000001B509000001000000012000C888B0E0E0FA62D089028307 a=control:trackID=0 //trackID=0 указывает, что видеопоток использует канал 0
Клиент предлагает серверу установить сеанс и определить режим передачи:
SETUP rtsp://192.168.20.136:5000/xxx666/trackID=0 RTSP/1.0
CSeq: 3
Transport: RTP/AVP/TCP;unicast;interleaved=0-1
User-Agent: VLC media player (LIVE555 Streaming Media v2005.11.10)
//в ури TrackID=0 означает настройку канала. Параметр Transport задает режим передачи и структуру пакета. Позиция второго байта заголовка следующего пакета данных равна interleaved,Его значение различно для каждого канала.,Перемеженное значение trackID=0 имеет два значения 0 или 1.,0 указывает rtp-пакет,1 означает пакет rtcp,Принимающая сторона различает тип пакета данных на основе значения чередования.
Информация об ответе сервера:
RTSP/1.0 200 OK
Server: UServer 0.9.7_rc1
Cseq: 3
Session: 6310936469860791894 //Идентификатор сеанса, полученный от сервера
Cache-Control: no-cache
Transport: RTP/AVP/TCP;unicast;interleaved=0-1;ssrc=6B8B4567
Клиент отправляет запрос на воспроизведение:
PLAY rtsp://192.168.20.136:5000/xxx666 RTSP/1.0
CSeq: 4
Session: 6310936469860791894
Range: npt=0.000- //Устанавливаем временной диапазон воспроизведения
User-Agent: VLC media player (LIVE555 Streaming Media v2005.11.10)
Информация об ответе сервера:
RTSP/1.0 200 OK
Server: UServer 0.9.7_rc1
Cseq: 4
Session: 6310936469860791894
Range: npt=0.000000-
RTP-Info: url=trackID=0;seq=17040;rtptime=1467265309
//seq и rtptime — это информация в пакете rtp
Клиент инициирует запрос на выключение:
TEARDOWN rtsp://192.168.20.136:5000/xxx666 RTSP/1.0
CSeq: 5
Session: 6310936469860791894
User-Agent: VLC media player (LIVE555 Streaming Media v2005.11.10)
Ответ сервера:
RTSP/1.0 200 OK
Server: UServer 0.9.7_rc1
Cseq: 5
Session: 6310936469860791894
Status-Code = "100" ; Continue
| "200" ; OK
| "201" ; Created
| "250" ; Low on Storage Space
| "300" ; Multiple Choices
| "301" ; Moved Permanently
| "302" ; Moved Temporarily
| "303" ; See Other
| "304" ; Not Modified
| "305" ; Use Proxy
| "400" ; Bad Request
| "401" ; Unauthorized
| "402" ; Payment Required
| "403" ; Forbidden
| "404" ; Not Found
| "405" ; Method Not Allowed
| "406" ; Not Acceptable
| "407" ; Proxy Authentication Required
| "408" ; Request Time-out
| "410" ; Gone
| "411" ; Length Required
| "412" ; Precondition Failed
| "413" ; Request Entity Too Large
| "414" ; Request-URI Too Large
| "415" ; Unsupported Media Type
| "451" ; Parameter Not Understood
| "452" ; Conference Not Found
| "453" ; Not Enough Bandwidth
| "454" ; Session Not Found
| "455" ; Method Not Valid in This State
| "456" ; Header Field Not Valid for Resource
| "457" ; Invalid Range
| "458" ; Parameter Is Read-Only
| "459" ; Aggregate operation not allowed
| "460" ; Only aggregate operation allowed
| "461" ; Unsupported transport
| "462" ; Destination unreachable
| "500" ; Internal Server Error
| "501" ; Not Implemented
| "502" ; Bad Gateway
| "503" ; Service Unavailable
| "504" ; Gateway Time-out
| "505" ; RTSP Version not supported
| "551" ; Option not supported
| extension-code
extension-code = 3DIGIT
Reason-Phrase = *<TEXT, excluding CR, LF
формат сдп:
Описание SDP состоит из множества строк текста.,文本行из格式为<тип>=<ценить>,<тип>это письмо,<ценить>是结构化из文本串,Его формат основан на<тип>и定。
<type>=<value>[CRLF]
v=<version>
o=<username> <session id> <version> <network type> <address type> <address>
s=<session name>
i=<session description>
u=<URI>
e=<email address>
p=<phone number>
c=<network type> <address type> <connection address>
b=<modifier>:<bandwidth-value>
t=<start time> <stop time>
r=<repeat interval> <active duration> <list of offsets from start-time>
z=<adjustment time> <offset> <adjustment time> <offset> ....
k=<method>
k=<method>:<encryption key>
a=<attribute>
a=<attribute>:<value>
m=<media> <port> <transport> <fmt list>
v = (версия протокола)
o = (владелец/создатель и идентификатор сеанса)
s = (название сеанса)
i = * (информация о сеансе)
u = * (URI описывать)
e = * (Email адрес)
p = * (номер телефона)
c = * (Информация о подключении)
b = * (информация о пропускной способности)
z = * (регулировка часового пояса)
k = * (ключ шифрования)
a = * (0 или более строк атрибутов сеанса)
SDP — это полностью формат описания сеанса, а не транспортный протокол — он использует только различные соответствующие транспортные протоколы, включая протокол объявления сеанса (SAP), протокол инициирования сеанса (SIP), протокол потоковой передачи в реальном времени (RTSP), электронную почту с расширениями MIME. а также протокол передачи гипертекста (HTTP). Протокол SDP также является текстовым протоколом, что обеспечивает относительно высокую масштабируемость протокола, что делает его имеющим широкий спектр приложений. SDP не поддерживает согласование содержимого сеанса или кодирования мультимедиа, поэтому он используется только для описания медиаинформации в потоковом мультимедиа. Согласование мультимедиа должно быть реализовано с использованием RTSP.
Ниже приведен протокол SDP в RTSP-протоколе для сервера потоковой передачи Helix:
v=0 //SDP version
// o field定义из源из一些信息。Его формат:o=<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address>
o=- 1271659412 1271659412 IN IP4 10.56.136.37 s=<No title>
i=<No author> <No copyright> //информация о сеансе
c=IN IP4 0.0.0.0 //connect Информация соответственно описывает: сетевой протокол, тип адреса и адрес подключения.
c=IN IP4 0.0.0.0
t=0 0 //Информация о времени, указывающая соответственно время начала и время окончания, обычно отображается во временном сдвиге потокового мультимедиа в реальном времени.
a=SdpplinVersion:1610641560 //описательная информация
a=StreamCount:integer;2 //Информация, используемая для описания медиапотока, указывающая на наличие двух медиапотоков. целое число указывает, что формат информации является целым числом.
a=control:*
a=DefaultLicenseValue:integer;0 //Информация о лицензии
a=FileType:string;"MPEG4" ////Информация, используемая для описания медиапотока, указывает, что текущий согласованный файл представляет собой файл в формате mpeg4.
a=LicenseKey:string;"license.Summary.Datatypes.RealMPEG4.Enabled"
a=range:npt=0-72.080000 //Используется для указания длины медиапотока
m=audio 0 RTP/AVP 96 //Являясь важной частью информации описания мультимедиа, он описывает подробное содержание медиаинформации: аудио, представляющее сеанс, передается в формате RTP, а его значение полезной нагрузки равно 96. Порт для передачи еще не определен.
b=as:24 //audio битрейт
b=RR:1800
b=RS:600
a=control:streamid=1 //Отправляем аудио через медиапоток 1
a=range:npt=0-72.080000 //Описываем длину медиапотока.
a=length:npt=72.080000
a=rtpmap:96 MPEG4-GENERIC/32000/2 //информация rtpmap, указывающая, что аудио имеет формат AAC и его семпл равен 32000
a=fmtp:96 profile-level-id=15;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config=1210 //config — подробная информация о формате AAC
a=mimetype:string;"audio/MPEG4-GENERIC"
a=Helix-Adaptation-Support:1
a=AvgBitRate:integer;48000
a=HasOutOfOrderTS:integer;1
a=MaxBitRate:integer;48000
a=Preroll:integer;1000
a=OpaqueData:buffer;"A4CAgCIAAAAEgICAFEAVABgAAAC7gAAAu4AFgICAAhKIBoCAgAEC"
a=StreamName:string;"Audio Track"
//Следующее заключается в том, что информация видео в основном симметрична информации аудио, что здесь не будет обсуждаться.
m=video 0 RTP/AVP 97
b=as:150
b=RR:11250
b=RS:3750
a=control:streamid=2
a=range:npt=0-72.080000
a=length:npt=72.080000
a=rtpmap:97 MP4V-ES/2500
a=fmtp:97 profile-level-id=1;
a=mimetype:string;"video/MP4V-ES"
a=Helix-Adaptation-Support:1
a=AvgBitRate:integer;300000
a=HasOutOfOrderTS:integer;1
a=Height:integer;240 //Длительность видео
a=MaxBitRate:integer;300000
a=MaxPacketSize:integer;1400
a=Preroll:integer;1000
a=Width:integer;320 //Ширина видео
a=OpaqueData:buffer;"AzcAAB8ELyARAbd0AAST4AAEk+AFIAAAAbDzAAABtQ7gQMDPAAABAAAAASAAhED6KFAg8KIfBgEC"
a=StreamName:string;"Video Track"
В процессе взаимодействия RTSP, пока клиент отправляет запрос описания, сообщение SDP будет отправлено при ответе сервера. SDP используется для описания ситуации и содержимого сеанса, чтобы клиент мог присоединиться к сеансу.
/*
* Copyright (c) 2011, Jim Hollinger
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Jim Hollinger nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* <DESC>
* A basic RTSP transfer
* </DESC>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#if defined (WIN32)
#include <conio.h> /* _getch() */
#else
#include <termios.h>
#include <unistd.h>
#define VERSION_STR "V1.0"
/* error handling macros */
#define my_curl_easy_setopt(A, B, C) \
res = curl_easy_setopt((A), (B), (C)); \
if(!res) \
fprintf(stderr, "curl_easy_setopt(%s, %s, %s) failed: %d\n", \
#A, #B, #C, res);
#define my_curl_easy_perform(A) \
res = curl_easy_perform(A); \
if(!res) \
fprintf(stderr, "curl_easy_perform(%s) failed: %d\n", #A, res);
static int _getch(void)
{
struct termios oldt, newt;
int ch;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~( ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
return ch;
}
#endif
/* send RTSP OPTIONS request */
static void rtsp_options(CURL *curl, const char *uri)
{
CURLcode res = CURLE_OK;
printf("\nRTSP: OPTIONS %s\n", uri);
my_curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, uri);
my_curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_OPTIONS);
my_curl_easy_perform(curl);
}
/* send RTSP DESCRIBE request and write sdp response to a file */
static void rtsp_describe(CURL *curl, const char *uri,
const char *sdp_filename)
{
CURLcode res = CURLE_OK;
FILE *sdp_fp = fopen(sdp_filename, "wb");
printf("\nRTSP: DESCRIBE %s\n", uri);
if(sdp_fp == NULL) {
fprintf(stderr, "Could not open '%s' for writing\n", sdp_filename);
sdp_fp = stdout;
}
else {
printf("Writing SDP to '%s'\n", sdp_filename);
}
my_curl_easy_setopt(curl, CURLOPT_WRITEDATA, sdp_fp);
my_curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_DESCRIBE);
my_curl_easy_perform(curl);
my_curl_easy_setopt(curl, CURLOPT_WRITEDATA, stdout);
if(sdp_fp != stdout) {
fclose(sdp_fp);
}
}
/* send RTSP SETUP request */
static void rtsp_setup(CURL *curl, const char *uri, const char *transport)
{
CURLcode res = CURLE_OK;
printf("\nRTSP: SETUP %s\n", uri);
printf(" TRANSPORT %s\n", transport);
my_curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, uri);
my_curl_easy_setopt(curl, CURLOPT_RTSP_TRANSPORT, transport);
my_curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_SETUP);
my_curl_easy_perform(curl);
}
/* send RTSP PLAY request */
static void rtsp_play(CURL *curl, const char *uri, const char *range)
{
CURLcode res = CURLE_OK;
printf("\nRTSP: PLAY %s\n", uri);
my_curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, uri);
my_curl_easy_setopt(curl, CURLOPT_RANGE, range);
my_curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_PLAY);
my_curl_easy_perform(curl);
}
/* send RTSP TEARDOWN request */
static void rtsp_teardown(CURL *curl, const char *uri)
{
CURLcode res = CURLE_OK;
printf("\nRTSP: TEARDOWN %s\n", uri);
my_curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_TEARDOWN);
my_curl_easy_perform(curl);
}
/* convert url into an sdp filename */
static void get_sdp_filename(const char *url, char *sdp_filename,
size_t namelen)
{
const char *s = strrchr(url, '/');
strcpy(sdp_filename, "video.sdp");
if(s != NULL) {
s++;
if(s[0] != '\0') {
snprintf(sdp_filename, namelen, "%s.sdp", s);
}
}
}
/* scan sdp file for media control attribute */
static void get_media_control_attribute(const char *sdp_filename,
char *control)
{
int max_len = 256;
char *s = malloc(max_len);
FILE *sdp_fp = fopen(sdp_filename, "rb");
control[0] = '\0';
if(sdp_fp != NULL) {
while(fgets(s, max_len - 2, sdp_fp) != NULL) {
sscanf(s, " a = control: %s", control);
}
fclose(sdp_fp);
}
free(s);
}
/* main app */
int main(int argc, char * const argv[])
{
#if 1
const char *transport = "RTP/AVP;unicast;client_port=1234-1235"; /* UDP */
#else
/* TCP */
const char *transport = "RTP/AVP/TCP;unicast;client_port=1234-1235";
#endif
const char *range = "0.000-";
int rc = EXIT_SUCCESS;
char *base_name = NULL;
printf("\nRTSP request %s\n", VERSION_STR);
printf(" Project web site: http://code.google.com/p/rtsprequest/\n");
printf(" Requires curl V7.20 or greater\n\n");
/* check command line */
if((argc != 2) && (argc != 3)) {
base_name = strrchr(argv[0], '/');
if(base_name == NULL) {
base_name = strrchr(argv[0], '\\');
}
if(base_name == NULL) {
base_name = argv[0];
}
else {
base_name++;
}
printf("Usage: %s url [transport]\n", base_name);
printf(" url of video server\n");
printf(" transport (optional) specifier for media stream"
" protocol\n");
printf(" default transport: %s\n", transport);
printf("Example: %s rtsp://192.168.0.2/media/video1\n\n", base_name);
rc = EXIT_FAILURE;
}
else {
const char *url = argv[1];
char *uri = malloc(strlen(url) + 32);
char *sdp_filename = malloc(strlen(url) + 32);
char *control = malloc(strlen(url) + 32);
CURLcode res;
get_sdp_filename(url, sdp_filename, strlen(url) + 32);
if(argc == 3) {
transport = argv[2];
}
/* initialize curl */
res = curl_global_init(CURL_GLOBAL_ALL);
if(res == CURLE_OK) {
curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
CURL *curl;
fprintf(stderr, " curl V%s loaded\n", data->version);
/* initialize this curl session */
curl = curl_easy_init();
if(curl != NULL) {
my_curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);
my_curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
my_curl_easy_setopt(curl, CURLOPT_HEADERDATA, stdout);
my_curl_easy_setopt(curl, CURLOPT_URL, url);
/* request server options */
snprintf(uri, strlen(url) + 32, "%s", url);
rtsp_options(curl, uri);
/* request session description and write response to sdp file */
rtsp_describe(curl, uri, sdp_filename);
/* get media control attribute from sdp file */
get_media_control_attribute(sdp_filename, control);
/* setup media stream */
snprintf(uri, strlen(url) + 32, "%s/%s", url, control);
rtsp_setup(curl, uri, transport);
/* start playing media stream */
snprintf(uri, strlen(url) + 32, "%s/", url);
rtsp_play(curl, uri, range);
printf("Playing video, press any key to stop ...");
_getch();
printf("\n");
/* teardown session */
rtsp_teardown(curl, uri);
/* cleanup */
curl_easy_cleanup(curl);
curl = NULL;
}
else {
fprintf(stderr, "curl_easy_init() failed\n");
}
curl_global_cleanup();
}
else {
fprintf(stderr, "curl_global_init(%s) failed: %d\n",
"CURL_GLOBAL_ALL", res);
}
free(control);
free(sdp_filename);
free(uri);
}
return rc;
}
Издатель: Лидер стека программистов полного стека, укажите источник для перепечатки: https://javaforall.cn/155275.html Исходная ссылка: https://javaforall.cn