RTSP-протокол
RTSP-протокол

Всем привет, мы снова встретились, я ваш друг Цюаньчжаньцзюнь.

1. Введение в RTSP

RTSP (протокол потоковой передачи в реальном времени) — это протокол прикладного уровня, совместно предложенный Real Network и Netscape для эффективной передачи потокового мультимедиа в IP-сетях. RTSP обеспечивает такие элементы управления, как приостановка и быстрая пересылка потокового мультимедиа, но сам он не передает данные. Роль RTSP эквивалентна удаленному управлению сервером потокового мультимедиа. Сервер может использовать TCP или UDP для передачи потокового контента. Его синтаксис и работа аналогичны HTTP1.1, но он не уделяет особого внимания синхронизации времени, поэтому он более терпим к сетевым задержкам.

2. Разница и связь между RTSP и HTTP

  • соединять:Оба используют обычный текст для отправки сообщений.,А синтаксис RTSP-протокола аналогичен HTTP. RTSP изначально был разработан так,Он также должен быть совместим с ранее написанным кодом анализа протокола HTTP.
  • разница:rstpС состоянием,Разница в том, что команде RTSP необходимо знать, в каком состоянии она находится в данный момент.,То естьRTSPКоманда всегдаОтправить по порядкуиз,Команда всегда отправляется перед другой командой。RTSPНезависимо от того, в каком состоянии вы находитесьНе отключится。иHTTPСостояние не сохраняется,протокол после отправки команды,Соединение будет разорвано,и между командаминет зависимостей,RTSP-протокол использует порт 544.,Протокол HTTP использует порт 80.

3. Связь между RTSP и RTP (TRCP)

  • RTP: протокол передачи данных в реальном времени. RTP предоставляет метки времени, порядковые номера и другие методы, гарантирующие обработку времени во время передачи данных в реальном времени.
  • RTCP: протокол управления транспортировкой в ​​реальном времени. Протокол управления транспортировкой в ​​реальном времени. RCTP является управляющей частью RTP и используется для обеспечения качества обслуживания и управления участниками. RTP и RTCP используются вместе.
  • RTSP: протокол потоковой передачи в реальном времени. Протокол потоковой передачи в реальном времени. Специальная передача данных RTSP RTP, обеспечивающая контроль конвекции.

RTP основан на протоколе UDP. UDP не требует установления соединения и является более эффективным. Но допускается потеря пакетов, что требует немного больше работы при повторной сборке носителя. RTP только упаковывает информацию о содержимом, тогда как RTCP обменивается управляющей информацией, а Qos реализуется через RTCP.

Приложение соответствует таким командам, как воспроизведение, поиск, пауза, остановка и т. д. RTSP обрабатывает эти команды и использует RTP (RTCP) во время передачи UDP. Если это TCP-соединение, RTP (RTCP) использоваться не будет.

Сервер подключения клиента RTSP передается через SDP (протокол описания сеанса).

4. RTSP-сообщение

Существует два основных типа сообщений RTSP: одно — сообщение запроса (запрос), а другое — ответное сообщение (ответ). Форматы этих двух сообщений различны.

1) Формат сообщения запроса

Язык кода:javascript
копировать
метод URI RTSP-версия CR LF
заголовок сообщения CR LF CR LF
тело сообщения CR LF

Методы включают: ОПЦИИ, НАСТРОЙКА, ВОСПРОИЗВЕДЕНИЕ, РАЗБОР, ОПИСАНИЕ.

URI — адрес получателя (сервера), например: rtsp://192.168.6.136:5000/v0

CR LF после каждой строки означает возврат каретки и перевод строки, что требует соответствующего анализа на принимающей стороне. Заголовок сообщения должен иметь два CR LF.

Язык кода:javascript
копировать
DESCRIBE rtsp://192.168.1.211 RTSP/1.0
CSeq: 1
Accept: application/sdp
User-Agent: magnus-fc

2) Формат ответного сообщения

Язык кода:javascript
копировать
RTSP-версия код состояния объяснять CR LF
заголовок сообщения CR LF CR LF
тело сообщения CR LF

Версия RTSP обычно RTSP/1.0, код состояния представляет собой числовое значение, 200 означает успех, а объяснение представляет собой текстовое объяснение, соответствующее коду состояния. Подробную информацию см. Введение в протокол SDP.

Язык кода:javascript
копировать
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

5. Процесс взаимодействия по RTSP

C означает rtsp-клиент, S означает rtsp-сервер.

step1:

Язык кода:javascript
копировать
C->S:OPTION request //Спрашиваем S, какие методы доступны
S->C:OPTION response //S回应信息中包括提供из所有可用метод

step2:

Язык кода:javascript
копировать
C->S:DESCRIBE request //Требуем информацию описания инициализации носителя, предоставленную S
S->C:DESCRIBE response //S отвечает на информацию описания инициализации носителя, в основном sdp

step3:

Язык кода:javascript
копировать
C->S:SETUP request //Устанавливаем атрибуты сеанса и режим передачи, чтобы напомнить S о необходимости установить сеанс
S->C:SETUP response //S устанавливаем сеанс,Возвращает идентификатор сеанса,и информация, связанная с сеансом

step4:

Язык кода:javascript
копировать
C->S:PLAY request //C запрашивает воспроизведение
S->C:PLAY response //S отвечаем на запрос информации
S->C: //Отправляем потоковые данные

step5:

Язык кода:javascript
копировать
C->S:TEARDOWN request //C запрос на закрытие сессии
S->C:TEARDOWN response //S отвечает на запрос

Процесс преобразования статуса команды выглядит следующим образом:

6. Основные методы RTSP

Описание метода:

1)OPTION

Получить доступные методы, предоставляемые сервером

Язык кода:javascript
копировать
OPTIONS rtsp://192.168.20.136:5000/xxx666 RTSP/1.0
CSeq: 1 //Каждое сообщение помечается порядковым номером. Первый пакет обычно представляет собой сообщение с запросом опции.
User-Agent: VLC media player (LIVE555 Streaming Media v2005.11.10)

Информация об ответе сервера включает в себя некоторые из предоставленных методов, например:

Язык кода:javascript
копировать
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 //服务器提供из可用изметод

2)DESCRIBE

C инициирует запрос DESCRIBE к S, чтобы получить информацию описания сеанса (SDP):

Язык кода:javascript
копировать
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) для этого сеанса:

Язык кода:javascript
копировать
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

3)SETUP

Клиент предлагает серверу установить сеанс и определить режим передачи:

Язык кода:javascript
копировать
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,Принимающая сторона различает тип пакета данных на основе значения чередования.

Информация об ответе сервера:

Язык кода:javascript
копировать
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

4)PLAY

Клиент отправляет запрос на воспроизведение:

Язык кода:javascript
копировать
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)

Информация об ответе сервера:

Язык кода:javascript
копировать
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

5)TEADDOWN

Клиент инициирует запрос на выключение:

Язык кода:javascript
копировать
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) 

Ответ сервера:

Язык кода:javascript
копировать
RTSP/1.0 200 OK 
Server: UServer 0.9.7_rc1 
Cseq: 5 
Session: 6310936469860791894 

7. Код состояния RTSP.

Язык кода:javascript
копировать
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

8. Протокол SDP

формат сдп:

Описание SDP состоит из множества строк текста.,文本行из格式为<тип>=<ценить>,<тип>это письмо,<ценить>是结构化из文本串,Его формат основан на<тип>и定。

<type>=<value>[CRLF]

Язык кода:javascript
копировать
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>
Язык кода:javascript
копировать
v = (версия протокола)
o = (владелец/создатель и идентификатор сеанса)
s = (название сеанса)
i = * (информация о сеансе)
u = * (URI описывать)
e = * (Email адрес)
p = * (номер телефона)
c = * (Информация о подключении)
b = * (информация о пропускной способности)
z = * (регулировка часового пояса)
k = * (ключ шифрования)
a = * (0 или более строк атрибутов сеанса)
  • Описание времени:
    • t=(активное время сеанса)
    • r=*(0 или более повторений)
  • Описание СМИ:
    • m = (имя носителя и транспортный адрес)
    • я = * (название носителя)
    • c = * (Информация о подключении — Это поле является необязательным, если оно включено в уровень сеанса)
    • b = * (информация о пропускной способности)
    • k = * (ключ шифрования)
    • a = * (0 или более строк атрибутов мультимедиа)

SDP — это полностью формат описания сеанса, а не транспортный протокол — он использует только различные соответствующие транспортные протоколы, включая протокол объявления сеанса (SAP), протокол инициирования сеанса (SIP), протокол потоковой передачи в реальном времени (RTSP), электронную почту с расширениями MIME. а также протокол передачи гипертекста (HTTP). Протокол SDP также является текстовым протоколом, что обеспечивает относительно высокую масштабируемость протокола, что делает его имеющим широкий спектр приложений. SDP не поддерживает согласование содержимого сеанса или кодирования мультимедиа, поэтому он используется только для описания медиаинформации в потоковом мультимедиа. Согласование мультимедиа должно быть реализовано с использованием RTSP.

Ниже приведен протокол SDP в RTSP-протоколе для сервера потоковой передачи Helix:

Язык кода:javascript
копировать
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"

9. Резюме

В процессе взаимодействия RTSP, пока клиент отправляет запрос описания, сообщение SDP будет отправлено при ответе сервера. SDP используется для описания ситуации и содержимого сеанса, чтобы клиент мог присоединиться к сеансу.

10. RTSP реализован на основе кода libcurl.

Язык кода:javascript
копировать
/*
 * 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

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose