У меня есть следующий код:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <termios.h>
#include <getopt.h>
#include <stdbool.h>
#include <curl/curl.h>
#include <pthread.h>
#define BUFFER_SIZE 1024
#define SW_URL_STR_lEN 255
struct test_url {
char protocol[SW_URL_STR_lEN];
char hostname[SW_URL_STR_lEN];
char page[SW_URL_STR_lEN];
int port;
};
typedef struct test_url test_url_t;
test_url_t *test_create_url();
void test_destroy_url(test_url_t *url);
bool test_parse_url(test_url_t *url, const char *to_parse);
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;
}
static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) {
(void) ptr;
if (size * nmemb > BUFFER_SIZE) {
fprintf(stdout, "Error buffer too small");
exit(EXIT_FAILURE);
}
char *buf = (char *) userdata;
strncpy(buf, ptr, size * nmemb);
return size * nmemb;
}
static void send_rtsp_options_request(CURL *handle, const char *uri) {
fprintf(stdout, "\nRTSP: OPTIONS %s\n", uri);
curl_easy_setopt(handle, CURLOPT_RTSP_STREAM_URI, uri);
curl_easy_setopt(handle, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_OPTIONS);
curl_easy_perform(handle);
}
static void send_rtsp_describe_request(CURL *handle, const char *uri, char *str_sdp) {
fprintf(stdout, "\nRTSP: DESCRIBE %s\n", uri);
fprintf(stdout, " describe: %s", str_sdp);
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(handle, CURLOPT_WRITEDATA, str_sdp);
curl_easy_setopt(handle, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_DESCRIBE);
curl_easy_perform(handle);
curl_easy_setopt(handle, CURLOPT_WRITEDATA, stdout);
}
static void send_rtsp_setup_request(CURL *handle, const char *uri, const char *transport) {
fprintf(stdout, "\nRTSP: SETUP %s\n", uri);
fprintf(stdout, " TRANSPORT %s\n", transport);
curl_easy_setopt(handle, CURLOPT_RTSP_STREAM_URI, uri);
curl_easy_setopt(handle, CURLOPT_RTSP_TRANSPORT, transport);
curl_easy_setopt(handle, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_SETUP);
curl_easy_perform(handle);
}
static void send_rtsp_play_request(CURL *handle, const char *uri, const char *range) {
fprintf(stdout, "\nRTSP: PLAY %s\n", uri);
curl_easy_setopt(handle, CURLOPT_RTSP_STREAM_URI, uri);
curl_easy_setopt(handle, CURLOPT_RANGE, range);
curl_easy_setopt(handle, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_PLAY);
curl_easy_perform(handle);
/* Disable ranges */
curl_easy_setopt(handle, CURLOPT_RANGE, NULL);
}
// static void send_rtsp_pause_request(CURL *handken, const char *uri, const char *range) {
// fprintf(stdout, "\nRTSP: PAUSE %s\n", uri);
// curl_easy_setopt(handle, CURLOPT_RTSP_STREAM_URI, uri);
// curl_easy_setopt(handle, CURLOPT_RANGE, range);
// curl_easy_setopt(handle, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_PAUSE);
// curl_easy_perform(handle);
// /* Disable ranges */
// curl_easy_setopt(handle, CURLOPT_RANGE, NULL);
// }
static void send_rtsp_teardown_request(CURL *handle, const char *uri) {
fprintf(stdout, "\nRTSP: TEARDOWN %s\n", uri);
curl_easy_setopt(handle, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_TEARDOWN);
curl_easy_perform(handle);
}
/* Main options */
typedef enum {
rtsp_injector_arg_help = 'h',
rtsp_injector_arg_addr = 'a',
rtsp_injector_arg_verbose = 'v',
} rtsp_injector_arg_t;
static void rtsp_print_help() {
fprintf(stdout, "Usage: \n");
fprintf(stdout, "\t-%c, --help\n", rtsp_injector_arg_help);
fprintf(stdout, "\t\tprints this help, and exits\n");
fprintf(stdout, "\n");
fprintf(stdout, "\t-%c, --rtsp-addr\n", rtsp_injector_arg_addr);
fprintf(stdout, "\t\tThe rtsp address of the server, with the port and the right camera choice such as \
\n\t\tActive camera - 'rtsp://(device ip address):5554/camera' \
\n\t\tBack camera - 'rtsp://(device ip address):5554/back' \
\n\t\tFront camera - 'rtsp://(device ip address):5554/front'\n");
fprintf(stdout, "\n");
fprintf(stdout, "\t-%c, --verbose\n", rtsp_injector_arg_verbose);
fprintf(stdout, "\t\tprints more details in the packets trading\n");
fprintf(stdout, "\n");
}
static void *pthread_proc(void *arg) {
(void)arg;
fprintf(stdout, "COIN COIN\n");
return NULL;
}
int main(int argc, char **argv) {
if (argc < 2) {
rtsp_print_help();
return EXIT_FAILURE;
}
static struct option long_options[] = {
{"help", no_argument, NULL, rtsp_injector_arg_help },
{"rtsp-addr", required_argument, NULL, rtsp_injector_arg_addr },
{"verbose", no_argument, NULL, rtsp_injector_arg_verbose },
{ NULL, 0, NULL, 0 },
};
int option_index = 0;
int c = 0;
char opstring[1024] = {0};
size_t s = 0;
pthread_t thrd;
// pthread_create(&thrd, NULL, pthread_proc, NULL);
s += snprintf(opstring + s, sizeof(opstring) - s, "%c", rtsp_injector_arg_help);
s += snprintf(opstring + s, sizeof(opstring) - s, "%c:", rtsp_injector_arg_addr);
s += snprintf(opstring + s, sizeof(opstring) - s, "%c", rtsp_injector_arg_verbose);
long verbose_is_on = 0L;
char *url = NULL;
while ((c = getopt_long(argc, argv, opstring, long_options, &option_index)) != -1) {
switch (c) {
case rtsp_injector_arg_help:
rtsp_print_help();
return EXIT_SUCCESS;
case rtsp_injector_arg_addr:
url = optarg;
break;
case rtsp_injector_arg_verbose:
verbose_is_on = 1L;
break;
default:
fprintf(stdout, "Invalid option 0%o: '%c' ??\n", c, c);
rtsp_print_help();
return EXIT_FAILURE;
}
}
if (url == NULL) {
fprintf(stdout, "Missing mandatory prefix '-a' to the server address\n");
rtsp_print_help();
return EXIT_FAILURE;
}
int res = 0;
/* Where the PLAY starts and when should it ends */
const char *range = "0.000-";
char uri[1024] = {0};
// char control[1024] = {0};
char str_sdp[BUFFER_SIZE] = {0};
/* Initialize all libcurl sub moduless */
res = curl_global_init(CURL_GLOBAL_ALL);
if (res != CURLE_OK) {
fprintf(stdout, "curl_global_init failed\n");
return EXIT_FAILURE;
}
/* Create a sesion handle */
CURL *my_handle = curl_easy_init();
if (my_handle == NULL) {
fprintf(stdout, "curl_easy_init failed\n");
return EXIT_FAILURE;
}
/* Set the url to work with, must to be the right format ie RFC , the function doesn't check it */
curl_easy_setopt(my_handle, CURLOPT_URL, url);
/* Parse server information ie ip and port */
test_url_t *my_url = test_create_url();
test_parse_url(my_url, url);
fprintf(stdout, "Protocol: %s\nHostname: %s\nPort: %d\nPage: %s\n",
my_url->protocol, my_url->hostname, my_url->port, my_url->page);
curl_easy_setopt(my_handle, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(my_handle, CURLOPT_HEADERDATA, stdout);
curl_easy_setopt(my_handle, CURLOPT_USERAGENT, "test-client-rtsp");
curl_easy_setopt(my_handle, CURLOPT_VERBOSE, verbose_is_on);
/* Let's send some requests */
snprintf(uri, sizeof(uri), "%s", url);
/* OPTIONS */
send_rtsp_options_request(my_handle, uri);
/* DESCRIBE */
send_rtsp_describe_request(my_handle, uri, str_sdp);
fprintf(stdout, "Sdp information:\n%s", str_sdp);
// char *transport = "RTP/AVP/TCP;unicast;client_port=1234-1235";
char transport[1024] = "RTP/AVP;unicast;client_port=1234-1235";
fprintf(stdout, "Transport: %s", transport);
/* SETUP */
snprintf(uri, sizeof(uri), "%s", url);
strncat(uri, "/trackID=0", strlen(uri)); /* Different url for SETUP, check doc in trac with the scenari exemple */
send_rtsp_setup_request(my_handle, uri, transport);
/* PLAY */
snprintf(uri, sizeof(uri), "%s", url);
send_rtsp_play_request(my_handle, uri, range);
pthread_create(&thrd, NULL, pthread_proc, NULL);
_getch();
/* TEARDOWN */
send_rtsp_teardown_request(my_handle, uri);
/* Destroy the session handle */
curl_easy_cleanup(my_handle);
/* Cleanup curl init resources */
curl_global_cleanup();
test_destroy_url(my_url);
return EXIT_SUCCESS;
}
test_url_t * test_create_url() {
test_url_t *ret = NULL;
ret = malloc(sizeof(test_url_t));
if (!ret) {
return NULL;
}
memset(ret, 0, sizeof(test_url_t));
return ret;
}
void test_destroy_url(test_url_t *url) {
free(url);
}
bool test_parse_url(test_url_t *url, const char *to_parse) {
int pos_of_hostname = 0, pos_port = 0, pos_page = 0;
const char *hostname = NULL;
const char *str_port = NULL;
const char *str_page = NULL;
char str_port_number[4];
if (to_parse == NULL) {
return false;
}
hostname = strstr(to_parse, "://");
if (hostname == NULL) {
return false;
}
pos_of_hostname = hostname - to_parse; /* Get the position of "://" */
if (pos_of_hostname > (int)sizeof(url->protocol)) {
return false;
}
strncpy(url->protocol, to_parse, sizeof(url->protocol));
url->protocol[pos_of_hostname] = '\0';
hostname = to_parse + pos_of_hostname + 3; /* Skip protocol and '://' */
/* Check if port is provided */
str_port = strstr(hostname, ":");
if (str_port != NULL) {
pos_port = str_port - hostname;
strncpy(url->hostname, hostname, sizeof(url->hostname));
url->hostname[pos_port] = '\0';
str_page = strstr(hostname, "/");
if (str_page != NULL) { /* Check if page is provided */
pos_page = str_page - hostname;
str_page = hostname + pos_page + 1;
strncpy(url->page, str_page, sizeof(url->page));
if (pos_port < pos_page) { /* Check if ':' is before '/' ex: ':8080/something' */
strncpy(url->hostname, hostname, sizeof(url->hostname));
url->hostname[pos_port] = '\0';
/* Port number is provided */
str_page = hostname + pos_port + 1; /* Skip hostname and the ':' before port number' */
strncpy(str_port_number, str_page, sizeof(str_port_number));
url->port = atoi(str_port_number);
}
else { /* Page provided before port */
/* Warning: strncpy doesn't put a '\0' at the end of the string if src doesn't have one*/
strncpy(url->hostname, hostname, sizeof(url->hostname));
url->hostname[pos_page] = '\0';
}
}
else { /* There is no page provided */
str_page = hostname + pos_port + 1;
strncpy(str_port_number, str_page, sizeof(str_port_number));
url->port = atoi(str_port_number);
}
}
else { /* Only page provided */
str_page = strstr(hostname, "/");
if (str_page != NULL) { /* Check if page is provided */
pos_page = str_page - hostname;
str_page = hostname + pos_page + 1;
strncpy(url->page, str_page, sizeof(url->page));
url->page[strlen(str_page)] = '\0';
strncpy(url->hostname, hostname, sizeof(url->hostname));
url->hostname[pos_page] = '\0';
}
else { /* Case where there is only a hostname provided */
strncpy(url->hostname, hostname, sizeof(url->hostname));
}
}
return true;
}
Код падает в строке 256 pthread_create со следующей обратной трассировкой:
Program received signal SIGSEGV, Segmentation fault.
__GI__IO_enable_locks () at genops.c:552
552 genops.c: No such file or directory.
(gdb) bt
#0 __GI__IO_enable_locks () at genops.c:552
#1 0x00007ffff7bbe52d in __pthread_create_2_1 (newthread=<optimized out>, attr=0x0, start_routine=<optimized out>, arg=0x0) at pthread_create.c:736
#2 0x0000555555557db5 in main (argc=3, argv=0x7fffffffd988) at test_client_rtsp.c:256
Если я раскомментирую первый pthread_create и прокомментирую второй, он работает нормально. Я так понимаю, это связано с libcurl, но я не слишком уверен в этом.
Curl 7.64.0 компилируется с:
./configure --disable-shared --enable-static --disable-ldap --disable-sspi --without-librtmp --disable-ftp --disable-file --disable-dict --disable-telnet --disable-tftp --disable-rtsp --disable-pop3 --disable-imap --disable-smtp --disable-gopher --disable-smb --without-libidn --without-zlib --without-ssl --enable-rtsp
Я немного растерялся относительно того, что там происходит.