mirror of
https://github.com/outbackdingo/UltraGrid.git
synced 2026-03-22 06:40:26 +00:00
Fixed a crash when init fails and done function is called - check if threads that are to be destroyed were actually created.
1147 lines
36 KiB
C++
1147 lines
36 KiB
C++
/*
|
|
* AUTHOR: Gerard Castillo <gerard.castillo@i2cat.net>,
|
|
* Martin German <martin.german@i2cat.net>
|
|
*
|
|
* Copyright (c) 2005-2010 Fundació i2CAT, Internet I Innovació Digital a Catalunya
|
|
* Copyright (c) 2015-2021 CESNET
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, is permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. 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.
|
|
*
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
*
|
|
* This product includes software developed by the University of Southern
|
|
* California Information Sciences Institute.
|
|
*
|
|
* 4. Neither the name of the University nor of the Institute may be used
|
|
* to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESSED 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 AUTHORS 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.
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "config_unix.h"
|
|
#include "config_win32.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <pthread.h>
|
|
#include <glib.h>
|
|
|
|
#include "audio/types.h"
|
|
#include "debug.h"
|
|
#include "host.h"
|
|
#include "lib_common.h"
|
|
#include "tv.h"
|
|
#include "rtp/rtp.h"
|
|
#include "rtp/rtp_callback.h"
|
|
#include "rtp/rtpdec_h264.h"
|
|
#include "rtsp/rtsp_utils.h"
|
|
|
|
#include "video_decompress.h"
|
|
|
|
#include "pdb.h"
|
|
#include "rtp/pbuf.h"
|
|
|
|
#include "video.h"
|
|
#include "video_codec.h"
|
|
#include "video_capture.h"
|
|
|
|
#include <curl/curl.h>
|
|
#include <chrono>
|
|
#include <memory>
|
|
|
|
#define KEEPALIVE_INTERVAL_S 5
|
|
#define MOD_NAME "[rtsp] "
|
|
#define VERSION_STR "V1.0"
|
|
|
|
//TODO set lower initial video recv buffer size (to find the minimal?)
|
|
#define DEFAULT_VIDEO_FRAME_WIDTH 1920
|
|
#define DEFAULT_VIDEO_FRAME_HEIGHT 1080
|
|
#define INITIAL_VIDEO_RECV_BUFFER_SIZE ((0.1*DEFAULT_VIDEO_FRAME_WIDTH*DEFAULT_VIDEO_FRAME_HEIGHT)*110/100) //command line net.core setup: sysctl -w net.core.rmem_max=9123840
|
|
|
|
|
|
/* error handling macros */
|
|
#define my_curl_easy_setopt(A, B, C, action_fail) \
|
|
if ((res = curl_easy_setopt((A), (B), (C))) != CURLE_OK){ \
|
|
fprintf(stderr, "[rtsp error] curl_easy_setopt(%s, %s, %s) failed: %s (%d)\n", #A, #B, #C, curl_easy_strerror(res), res); \
|
|
printf("[rtsp error] could not configure rtsp capture properly, \n\t\tplease check your parameters. \nExiting...\n\n"); \
|
|
action_fail; \
|
|
}
|
|
|
|
#define my_curl_easy_perform(A) \
|
|
if ((res = curl_easy_perform((A))) != CURLE_OK){ \
|
|
fprintf(stderr, "[rtsp error] curl_easy_perform(%s) failed: %s (%d)\n", #A, curl_easy_strerror(res), res); \
|
|
printf("[rtsp error] could not configure rtsp capture properly, \n\t\tplease check your parameters. \nExiting...\n\n"); \
|
|
return NULL; \
|
|
}
|
|
|
|
/* send RTSP GET_PARAMETERS request */
|
|
static int
|
|
rtsp_get_parameters(CURL *curl, const char *uri);
|
|
|
|
/* send RTSP OPTIONS request */
|
|
static int
|
|
rtsp_options(CURL *curl, const char *uri);
|
|
|
|
/* send RTSP DESCRIBE request and write sdp response to a file */
|
|
static bool
|
|
rtsp_describe(CURL *curl, const char *uri, FILE *sdp_fp);
|
|
|
|
/* send RTSP SETUP request */
|
|
static int
|
|
rtsp_setup(CURL *curl, const char *uri, const char *transport);
|
|
|
|
/* send RTSP PLAY request */
|
|
static int
|
|
rtsp_play(CURL *curl, const char *uri, const char *range);
|
|
|
|
/* send RTSP TEARDOWN request */
|
|
static int
|
|
rtsp_teardown(CURL *curl, const char *uri);
|
|
|
|
static int
|
|
get_nals(FILE *sdp_file, char *nals, int *width, int *height);
|
|
|
|
bool setup_codecs_and_controls_from_sdp(FILE *sdp_file, void *state);
|
|
|
|
static int
|
|
init_rtsp(struct rtsp_state *s);
|
|
|
|
static int
|
|
init_decompressor(struct video_rtsp_state *sr, struct video_desc desc);
|
|
|
|
static void *
|
|
vidcap_rtsp_thread(void *args);
|
|
|
|
static void
|
|
show_help(void);
|
|
|
|
void getNewLine(const char* buffer, int* i, char* line);
|
|
|
|
void
|
|
rtsp_keepalive(void *state);
|
|
|
|
int decode_frame_by_pt(struct coded_data *cdata, void *decode_data, struct pbuf_stats *);
|
|
|
|
static void vidcap_rtsp_done(void *state);
|
|
|
|
static const uint8_t start_sequence[] = { 0, 0, 0, 1 };
|
|
|
|
/**
|
|
* @struct rtsp_state
|
|
*/
|
|
struct video_rtsp_state {
|
|
const char *codec;
|
|
|
|
struct video_desc desc;
|
|
struct video_frame *out_frame;
|
|
|
|
//struct std_frame_received *rx_data;
|
|
bool decompress;
|
|
|
|
struct state_decompress *sd;
|
|
struct video_desc decompress_desc;
|
|
|
|
int port;
|
|
char *control;
|
|
|
|
struct rtp *device;
|
|
struct pdb *participants;
|
|
double rtcp_bw;
|
|
int ttl;
|
|
char *mcast_if;
|
|
int required_connections;
|
|
|
|
pthread_t vrtsp_thread_id; //the worker_id
|
|
|
|
pthread_mutex_t lock;
|
|
pthread_cond_t worker_cv;
|
|
volatile bool worker_waiting;
|
|
pthread_cond_t boss_cv;
|
|
volatile bool boss_waiting;
|
|
|
|
unsigned int h264_offset_len;
|
|
unsigned char *h264_offset_buffer;
|
|
|
|
int pt;
|
|
};
|
|
|
|
struct audio_rtsp_state {
|
|
struct audio_frame audio;
|
|
int play_audio_frame;
|
|
|
|
const char *codec;
|
|
|
|
struct timeval last_audio_time;
|
|
unsigned int grab_audio:1;
|
|
|
|
int port;
|
|
|
|
char *control;
|
|
|
|
struct rtp *device;
|
|
struct pdb *participants;
|
|
double rtcp_bw;
|
|
int ttl;
|
|
char *mcast_if;
|
|
int required_connections;
|
|
|
|
pthread_t artsp_thread_id; //the worker_id
|
|
pthread_mutex_t lock;
|
|
pthread_cond_t worker_cv;
|
|
volatile bool worker_waiting;
|
|
pthread_cond_t boss_cv;
|
|
volatile bool boss_waiting;
|
|
};
|
|
|
|
struct rtsp_state {
|
|
CURL *curl;
|
|
char uri[1024];
|
|
rtps_types_t avType;
|
|
const char *addr;
|
|
char *sdp;
|
|
|
|
volatile bool should_exit;
|
|
struct audio_rtsp_state artsp_state;
|
|
struct video_rtsp_state vrtsp_state;
|
|
|
|
pthread_t keep_alive_rtsp_thread_id; //the worker_id
|
|
pthread_mutex_t lock;
|
|
pthread_cond_t keepalive_cv;
|
|
};
|
|
|
|
static void curl_print_error(const char *msg, CURLcode err) {
|
|
error_msg("%s: %s\n", msg, curl_easy_strerror(err));
|
|
}
|
|
|
|
static void
|
|
show_help() {
|
|
printf("[rtsp] usage:\n");
|
|
printf("\t-t rtsp:<uri>[:rtp_rx_port=<port>][:decompress][size=<width>x<height>]\n");
|
|
printf("\t\t <uri> - RTSP server URI\n");
|
|
printf("\t\t <port> - receiver port number \n");
|
|
printf(
|
|
"\t\t decompress - decompress the stream (default: disabled)\n\n");
|
|
}
|
|
|
|
static void *
|
|
keep_alive_thread(void *arg){
|
|
struct rtsp_state *s = (struct rtsp_state *) arg;
|
|
|
|
pthread_mutex_lock(&s->lock);
|
|
while (1) {
|
|
struct timeval tp;
|
|
gettimeofday(&tp, NULL);
|
|
struct timespec timeout = { .tv_sec = tp.tv_sec + KEEPALIVE_INTERVAL_S, .tv_nsec = tp.tv_usec * 1000 };
|
|
pthread_cond_timedwait(&s->keepalive_cv, &s->lock, &timeout);
|
|
if (s->should_exit) {
|
|
pthread_mutex_unlock(&s->vrtsp_state.lock);
|
|
break;
|
|
}
|
|
pthread_mutex_unlock(&s->vrtsp_state.lock);
|
|
|
|
// actuall keepalive
|
|
if (rtsp_get_parameters(s->curl, s->uri) == 0) {
|
|
s->should_exit = TRUE;
|
|
exit_uv(1);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int decode_frame_by_pt(struct coded_data *cdata, void *decode_data, struct pbuf_stats *) {
|
|
rtp_packet *pckt = NULL;
|
|
pckt = cdata->data;
|
|
struct decode_data_h264 *d = (struct decode_data_h264 *) decode_data;
|
|
if (pckt->pt == d->video_pt) {
|
|
return decode_frame_h264(cdata,decode_data);
|
|
} else {
|
|
error_msg("Wrong Payload type: %u\n", pckt->pt);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static void *
|
|
vidcap_rtsp_thread(void *arg) {
|
|
struct rtsp_state *s;
|
|
s = (struct rtsp_state *) arg;
|
|
|
|
time_ns_t start_time = get_time_in_ns();
|
|
|
|
struct video_frame *frame = vf_alloc_desc_data(s->vrtsp_state.desc);
|
|
|
|
while (!s->should_exit) {
|
|
time_ns_t curr_time = get_time_in_ns();
|
|
uint32_t timestamp = (curr_time - start_time) / 100'000 * 9; // at 90000 Hz
|
|
|
|
rtp_update(s->vrtsp_state.device, curr_time);
|
|
|
|
struct timeval timeout;
|
|
timeout.tv_sec = 0;
|
|
timeout.tv_usec = 10000;
|
|
|
|
if (!rtp_recv_r(s->vrtsp_state.device, &timeout, timestamp)) {
|
|
pdb_iter_t it;
|
|
struct pdb_e *cp = pdb_iter_init(s->vrtsp_state.participants, &it);
|
|
|
|
while (cp != NULL) {
|
|
struct decode_data_h264 d;
|
|
d.frame = frame;
|
|
d.offset_len = s->vrtsp_state.h264_offset_len;
|
|
d.video_pt = s->vrtsp_state.pt;
|
|
if (pbuf_decode(cp->playout_buffer, curr_time,
|
|
decode_frame_by_pt, &d))
|
|
{
|
|
pthread_mutex_lock(&s->vrtsp_state.lock);
|
|
while (s->vrtsp_state.out_frame != NULL && !s->should_exit) {
|
|
s->vrtsp_state.worker_waiting = true;
|
|
pthread_cond_wait(&s->vrtsp_state.worker_cv, &s->vrtsp_state.lock);
|
|
s->vrtsp_state.worker_waiting = false;
|
|
}
|
|
if (s->vrtsp_state.out_frame == NULL) {
|
|
s->vrtsp_state.out_frame = frame;
|
|
frame = vf_alloc_desc_data(s->vrtsp_state.desc); // alloc new
|
|
if (s->vrtsp_state.boss_waiting)
|
|
pthread_cond_signal(&s->vrtsp_state.boss_cv);
|
|
pthread_mutex_unlock(&s->vrtsp_state.lock);
|
|
} else {
|
|
pthread_mutex_unlock(&s->vrtsp_state.lock);
|
|
}
|
|
}
|
|
pbuf_remove(cp->playout_buffer, curr_time);
|
|
cp = pdb_iter_next(&it);
|
|
}
|
|
|
|
pdb_iter_done(&it);
|
|
}
|
|
}
|
|
vf_free(frame);
|
|
return NULL;
|
|
}
|
|
|
|
static struct video_frame *
|
|
vidcap_rtsp_grab(void *state, struct audio_frame **audio) {
|
|
struct rtsp_state *s;
|
|
s = (struct rtsp_state *) state;
|
|
|
|
*audio = NULL;
|
|
|
|
if(pthread_mutex_trylock(&s->vrtsp_state.lock)==0){
|
|
{
|
|
while (s->vrtsp_state.out_frame == NULL && !s->should_exit) {
|
|
struct timeval tp;
|
|
gettimeofday(&tp, NULL);
|
|
struct timespec timeout = { .tv_sec = tp.tv_sec, .tv_nsec = (tp.tv_usec + 100*1000) * 1000 };
|
|
if (timeout.tv_nsec >= 1000L*1000*1000) {
|
|
timeout.tv_nsec -= 1000L*1000*1000;
|
|
timeout.tv_sec += 1;
|
|
}
|
|
s->vrtsp_state.boss_waiting = true;
|
|
if (pthread_cond_timedwait(&s->vrtsp_state.boss_cv, &s->vrtsp_state.lock, &timeout) == ETIMEDOUT) {
|
|
pthread_mutex_unlock(&s->vrtsp_state.lock);
|
|
return NULL;
|
|
}
|
|
s->vrtsp_state.boss_waiting = false;
|
|
}
|
|
|
|
if (s->should_exit) {
|
|
pthread_mutex_unlock(&s->vrtsp_state.lock);
|
|
return NULL;
|
|
}
|
|
|
|
struct video_frame *frame = s->vrtsp_state.out_frame;
|
|
s->vrtsp_state.out_frame = NULL;
|
|
pthread_mutex_unlock(&s->vrtsp_state.lock);
|
|
pthread_cond_signal(&s->vrtsp_state.worker_cv);
|
|
|
|
if(s->vrtsp_state.h264_offset_len>0 && frame->frame_type == INTRA){
|
|
memcpy(frame->tiles[0].data, s->vrtsp_state.h264_offset_buffer, s->vrtsp_state.h264_offset_len);
|
|
}
|
|
|
|
if (s->vrtsp_state.decompress) {
|
|
struct video_desc curr_desc = video_desc_from_frame(frame);
|
|
curr_desc.color_spec = H264;
|
|
if (!video_desc_eq(s->vrtsp_state.decompress_desc, curr_desc)) {
|
|
decompress_done(s->vrtsp_state.sd);
|
|
if (init_decompressor(&s->vrtsp_state, curr_desc) == 0) {
|
|
pthread_mutex_unlock(&s->vrtsp_state.lock);
|
|
return NULL;
|
|
}
|
|
s->vrtsp_state.decompress_desc = curr_desc;
|
|
}
|
|
|
|
struct video_desc out_desc = s->vrtsp_state.decompress_desc;
|
|
out_desc.color_spec = UYVY;
|
|
struct video_frame *decompressed = vf_alloc_desc_data(out_desc);
|
|
|
|
decompress_frame(s->vrtsp_state.sd, (unsigned char *) decompressed->tiles[0].data,
|
|
(unsigned char *) frame->tiles[0].data,
|
|
frame->tiles[0].data_len, 0, nullptr, nullptr);
|
|
vf_free(frame);
|
|
frame = decompressed;
|
|
}
|
|
frame->callbacks.dispose = vf_free;
|
|
return frame;
|
|
}
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
#define INIT_FAIL(msg) log_msg(LOG_LEVEL_ERROR, MOD_NAME msg); \
|
|
free(tmp); \
|
|
vidcap_rtsp_done(s); \
|
|
show_help(); \
|
|
return VIDCAP_INIT_FAIL
|
|
|
|
static int
|
|
vidcap_rtsp_init(struct vidcap_params *params, void **state) {
|
|
|
|
log_msg(LOG_LEVEL_WARNING, "RTSP capture module is most likely broken, "
|
|
"please contact " PACKAGE_BUGREPORT " if you wish to use it.\n");
|
|
|
|
if (vidcap_params_get_fmt(params)
|
|
&& strcmp(vidcap_params_get_fmt(params), "help") == 0)
|
|
{
|
|
show_help();
|
|
return VIDCAP_INIT_NOERR;
|
|
}
|
|
|
|
struct rtsp_state *s = (struct rtsp_state *) calloc(1, sizeof(struct rtsp_state));
|
|
if (s == NULL) {
|
|
return VIDCAP_INIT_FAIL;
|
|
}
|
|
|
|
//TODO now static codec assignment, to be dynamic as a function of supported codecs
|
|
s->vrtsp_state.codec = "";
|
|
s->artsp_state.codec = "";
|
|
s->artsp_state.control = strdup("");
|
|
s->artsp_state.control = strdup("");
|
|
|
|
int len = -1;
|
|
char *save_ptr = NULL;
|
|
s->avType = none; //-1 none, 0 a&v, 1 v, 2 a
|
|
|
|
s->addr = "127.0.0.1";
|
|
s->vrtsp_state.device = NULL;
|
|
s->vrtsp_state.rtcp_bw = 5 * 1024 * 1024; /* FIXME */
|
|
s->vrtsp_state.ttl = 255;
|
|
|
|
s->vrtsp_state.mcast_if = NULL;
|
|
s->vrtsp_state.required_connections = 1;
|
|
|
|
s->vrtsp_state.participants = pdb_init(0);
|
|
|
|
s->vrtsp_state.h264_offset_buffer = (unsigned char *) malloc(2048);
|
|
s->vrtsp_state.h264_offset_len = 0;
|
|
|
|
s->curl = NULL;
|
|
char *fmt = NULL;
|
|
|
|
pthread_mutex_init(&s->lock, NULL);
|
|
pthread_cond_init(&s->keepalive_cv, NULL);
|
|
pthread_mutex_init(&s->vrtsp_state.lock, NULL);
|
|
pthread_cond_init(&s->vrtsp_state.boss_cv, NULL);
|
|
pthread_cond_init(&s->vrtsp_state.worker_cv, NULL);
|
|
|
|
char *tmp, *item;
|
|
fmt = strdup(vidcap_params_get_fmt(params));
|
|
tmp = fmt;
|
|
strcpy(s->uri, "rtsp://");
|
|
|
|
s->vrtsp_state.desc.tile_count = 1;
|
|
s->vrtsp_state.desc.width = DEFAULT_VIDEO_FRAME_WIDTH/2;
|
|
s->vrtsp_state.desc.height = DEFAULT_VIDEO_FRAME_HEIGHT/2;
|
|
|
|
bool in_uri = true;
|
|
while ((item = strtok_r(fmt, ":", &save_ptr))) {
|
|
fmt = NULL;
|
|
bool option_given = true;
|
|
if (strstr(item, "rtp_rx_port=") == item) {
|
|
s->vrtsp_state.port = atoi(strchr(item, '=') + 1);
|
|
} else if (strcmp(item, "decompress") == 0) {
|
|
s->vrtsp_state.decompress = TRUE;
|
|
} else if (strstr(item, "size=")) {
|
|
assert(strchr(item, 'x') != NULL);
|
|
item = strchr(item, '=') + 1;
|
|
s->vrtsp_state.desc.width = atoi(item);
|
|
s->vrtsp_state.desc.height = atoi(strchr(item, 'x') + 1);
|
|
} else {
|
|
option_given = false;
|
|
if (in_uri) {
|
|
if (strcmp(item, "rtsp") == 0) { // rtsp:
|
|
continue;
|
|
}
|
|
if (strstr(item, "//") == item) { // rtsp://
|
|
item += 2;
|
|
}
|
|
if (strcmp(s->uri, "rtsp://") != 0) {
|
|
strncat(s->uri, ":", sizeof s->uri - strlen(s->uri) - 1);
|
|
}
|
|
strncat(s->uri, item, sizeof s->uri - strlen(s->uri) - 1);
|
|
} else {
|
|
INIT_FAIL("Unknown option\n");
|
|
}
|
|
}
|
|
if (option_given) {
|
|
in_uri = false;
|
|
}
|
|
}
|
|
free(tmp);
|
|
tmp = NULL;
|
|
|
|
//re-check parameters
|
|
if (strcmp(s->uri, "rtsp://") == 0) {
|
|
INIT_FAIL("No URI given!\n");
|
|
}
|
|
|
|
s->vrtsp_state.device = rtp_init_if("localhost", s->vrtsp_state.mcast_if, s->vrtsp_state.port, 0, s->vrtsp_state.ttl, s->vrtsp_state.rtcp_bw,
|
|
0, rtp_recv_callback, (uint8_t *) s->vrtsp_state.participants, 0, false);
|
|
if (s->vrtsp_state.device == NULL) {
|
|
log_msg(LOG_LEVEL_ERROR, "[rtsp] Cannot intialize RTP device!\n");
|
|
vidcap_rtsp_done(s);
|
|
return VIDCAP_INIT_FAIL;
|
|
}
|
|
if (!rtp_set_option(s->vrtsp_state.device, RTP_OPT_WEAK_VALIDATION, 1)) {
|
|
debug_msg("[rtsp] RTP INIT - set option\n");
|
|
return VIDCAP_INIT_FAIL;
|
|
}
|
|
if (!rtp_set_sdes(s->vrtsp_state.device, rtp_my_ssrc(s->vrtsp_state.device),
|
|
RTCP_SDES_TOOL, PACKAGE_STRING, strlen(PACKAGE_STRING))) {
|
|
debug_msg("[rtsp] RTP INIT - set sdes\n");
|
|
return VIDCAP_INIT_FAIL;
|
|
}
|
|
|
|
int ret = rtp_set_recv_buf(s->vrtsp_state.device, INITIAL_VIDEO_RECV_BUFFER_SIZE);
|
|
if (!ret) {
|
|
debug_msg("[rtsp] RTP INIT - set recv buf \nset command: sudo sysctl -w net.core.rmem_max=9123840\n");
|
|
return VIDCAP_INIT_FAIL;
|
|
}
|
|
|
|
if (!rtp_set_send_buf(s->vrtsp_state.device, 1024 * 56)) {
|
|
debug_msg("[rtsp] RTP INIT - set send buf\n");
|
|
return VIDCAP_INIT_FAIL;
|
|
}
|
|
ret=pdb_add(s->vrtsp_state.participants, rtp_my_ssrc(s->vrtsp_state.device));
|
|
|
|
debug_msg("[rtsp] rtp receiver init done\n");
|
|
|
|
if (s->vrtsp_state.port == 0) {
|
|
s->vrtsp_state.port = rtp_get_udp_rx_port(s->vrtsp_state.device);
|
|
assert(s->vrtsp_state.port != 0);
|
|
}
|
|
|
|
debug_msg("[rtsp] selected flags:\n");
|
|
debug_msg("\t uri: %s\n",s->uri);
|
|
debug_msg("\t port: %d\n", s->vrtsp_state.port);
|
|
debug_msg("\t decompress: %d\n\n",s->vrtsp_state.decompress);
|
|
|
|
len = init_rtsp(s);
|
|
|
|
if(len < 0){
|
|
vidcap_rtsp_done(s);
|
|
return VIDCAP_INIT_FAIL;
|
|
}else{
|
|
s->vrtsp_state.h264_offset_len = len;
|
|
}
|
|
|
|
//TODO fps should be autodetected, now reset and controlled at vidcap_grab function
|
|
s->vrtsp_state.desc.fps = 30;
|
|
s->vrtsp_state.desc.interlacing = PROGRESSIVE;
|
|
|
|
s->should_exit = FALSE;
|
|
|
|
s->vrtsp_state.boss_waiting = false;
|
|
s->vrtsp_state.worker_waiting = false;
|
|
|
|
if (s->vrtsp_state.decompress) {
|
|
struct video_desc decompress_desc = s->vrtsp_state.desc;
|
|
decompress_desc.color_spec = H264;
|
|
if (init_decompressor(&s->vrtsp_state, decompress_desc) == 0) {
|
|
vidcap_rtsp_done(s);
|
|
return VIDCAP_INIT_FAIL;
|
|
}
|
|
}
|
|
|
|
pthread_create(&s->vrtsp_state.vrtsp_thread_id, NULL, vidcap_rtsp_thread, s);
|
|
pthread_create(&s->keep_alive_rtsp_thread_id, NULL, keep_alive_thread, s);
|
|
|
|
debug_msg("[rtsp] rtsp capture init done\n");
|
|
|
|
*state = s;
|
|
return VIDCAP_INIT_OK;
|
|
}
|
|
|
|
static CURL *init_curl() {
|
|
CURL *curl;
|
|
/* initialize curl */
|
|
CURLcode res = curl_global_init(CURL_GLOBAL_ALL);
|
|
if (res != CURLE_OK) {
|
|
fprintf(stderr, "[rtsp] curl_global_init(%s) failed: %s (%d)\n",
|
|
"CURL_GLOBAL_ALL", curl_easy_strerror(res), res);
|
|
return NULL;
|
|
}
|
|
curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
|
|
fprintf(stderr, "[rtsp] cURL V%s loaded\n", data->version);
|
|
|
|
/* initialize this curl session */
|
|
curl = curl_easy_init();
|
|
if (curl == NULL) {
|
|
curl_global_cleanup();
|
|
fprintf(stderr, "[rtsp] curl_easy_init() failed\n");
|
|
return NULL;
|
|
}
|
|
return curl;
|
|
}
|
|
|
|
/**
|
|
* Initializes rtsp state and internal parameters
|
|
*/
|
|
static int
|
|
init_rtsp(struct rtsp_state *s) {
|
|
/* initialize curl */
|
|
s->curl = init_curl();
|
|
|
|
if (!s->curl) {
|
|
return -1;
|
|
}
|
|
|
|
const char *range = "0.000-";
|
|
int len_nals = -1;
|
|
debug_msg("\n[rtsp] request %s\n", VERSION_STR);
|
|
debug_msg(" Project web site: http://code.google.com/p/rtsprequest/\n");
|
|
debug_msg(" Requires cURL V7.20 or greater\n\n");
|
|
char Atransport[256];
|
|
char Vtransport[256];
|
|
memset(Atransport, 0, 256);
|
|
memset(Vtransport, 0, 256);
|
|
int port = s->vrtsp_state.port;
|
|
CURLcode res;
|
|
FILE *sdp_file = tmpfile();
|
|
if (sdp_file == NULL) {
|
|
sdp_file = fopen("rtsp.sdp", "w+");
|
|
if (sdp_file == NULL) {
|
|
perror("Creating SDP file");
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
sprintf(Vtransport, "RTP/AVP;unicast;client_port=%d-%d", port, port + 1);
|
|
|
|
//THIS AUDIO PORTS ARE AS DEFAULT UG AUDIO PORTS BUT AREN'T RELATED...
|
|
sprintf(Atransport, "RTP/AVP;unicast;client_port=%d-%d", port+2, port + 3);
|
|
|
|
my_curl_easy_setopt(s->curl, CURLOPT_NOSIGNAL, 1, goto error); //This tells curl not to use any functions that install signal handlers or cause signals to be sent to your process.
|
|
//my_curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, 1);
|
|
my_curl_easy_setopt(s->curl, CURLOPT_VERBOSE, 0L, goto error);
|
|
my_curl_easy_setopt(s->curl, CURLOPT_NOPROGRESS, 1L, goto error);
|
|
my_curl_easy_setopt(s->curl, CURLOPT_WRITEHEADER, stdout, goto error);
|
|
my_curl_easy_setopt(s->curl, CURLOPT_URL, s->uri, goto error);
|
|
|
|
//TODO TO CHECK CONFIGURING ERRORS
|
|
//CURLOPT_ERRORBUFFER
|
|
//http://curl.haxx.se/libcurl/c/curl_easy_perform.html
|
|
|
|
/* request server options */
|
|
if(rtsp_options(s->curl, s->uri)==0){
|
|
goto error;
|
|
}
|
|
|
|
/* request session description and write response to sdp file */
|
|
if (!rtsp_describe(s->curl, s->uri, sdp_file)) {
|
|
goto error;
|
|
}
|
|
|
|
if (log_level >= LOG_LEVEL_VERBOSE) {
|
|
fprintf(stderr, "SDP:\n");
|
|
while (!feof(sdp_file)) {
|
|
putc(getc(sdp_file), stderr);
|
|
}
|
|
rewind(sdp_file);
|
|
fprintf(stderr, "\n\n");
|
|
}
|
|
|
|
if (!setup_codecs_and_controls_from_sdp(sdp_file, s)) {
|
|
goto error;
|
|
}
|
|
if (strcmp(s->vrtsp_state.codec, "H264") == 0){
|
|
s->vrtsp_state.desc.color_spec = H264;
|
|
char uri[strlen(s->uri) + 1 + strlen(s->vrtsp_state.control) + 1];
|
|
strcpy(uri, s->uri);
|
|
strcat(uri, "/");
|
|
strcat(uri, s->vrtsp_state.control);
|
|
debug_msg("\n V URI = %s\n", uri);
|
|
if (rtsp_setup(s->curl, uri, Vtransport) == 0) {
|
|
goto error;
|
|
}
|
|
}
|
|
if (strcmp(s->artsp_state.codec, "PCMU") == 0){
|
|
char uri[strlen(s->uri) + 1 + strlen(s->artsp_state.control) + 1];
|
|
strcpy(uri, s->uri);
|
|
strcat(uri, "/");
|
|
strcat(uri, s->artsp_state.control);
|
|
debug_msg("\n A URI = %s\n", uri);
|
|
if (rtsp_setup(s->curl, uri, Atransport) == 0) {
|
|
goto error;
|
|
}
|
|
}
|
|
if (strlen(s->artsp_state.codec) == 0 && strlen(s->vrtsp_state.codec) == 0){
|
|
goto error;
|
|
}
|
|
else{
|
|
if(rtsp_play(s->curl, s->uri, range)==0){
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
/* get start nal size attribute from sdp file */
|
|
len_nals = get_nals(sdp_file, (char *) s->vrtsp_state.h264_offset_buffer, (int *) &s->vrtsp_state.desc.width, (int *) &s->vrtsp_state.desc.height);
|
|
|
|
debug_msg("[rtsp] playing video from server (size: WxH = %d x %d)...\n", s->vrtsp_state.desc.width,s->vrtsp_state.desc.height);
|
|
|
|
fclose(sdp_file);
|
|
return len_nals;
|
|
|
|
error:
|
|
if(sdp_file)
|
|
fclose(sdp_file);
|
|
return -1;
|
|
}
|
|
|
|
#define LEN 10
|
|
|
|
bool setup_codecs_and_controls_from_sdp(FILE *sdp_file, void *state) {
|
|
struct rtsp_state *rtspState;
|
|
rtspState = (struct rtsp_state *) state;
|
|
|
|
int n=0;
|
|
char *line = (char*) malloc(1024);
|
|
char* tmpBuff;
|
|
int countT = 0;
|
|
int countC = 0;
|
|
char codecs[2][LEN] = { { 0 } };
|
|
char tracks[2][LEN] = { { 0 } };
|
|
|
|
fseek(sdp_file, 0, SEEK_END);
|
|
long fileSize = ftell(sdp_file);
|
|
if (fileSize < 0) {
|
|
perror("RTSP ftell");
|
|
free(line);
|
|
return false;
|
|
}
|
|
rewind(sdp_file);
|
|
|
|
auto buffer = std::make_unique<char[]>(fileSize + 1);
|
|
unsigned long readResult = fread(buffer.get(), sizeof(char), fileSize, sdp_file);
|
|
if (ferror(sdp_file)){
|
|
perror(MOD_NAME "SDP file read failed");
|
|
free(line);
|
|
return false;
|
|
}
|
|
buffer[readResult] = '\0';
|
|
|
|
while (buffer[n] != '\0'){
|
|
getNewLine(buffer.get(),&n,line);
|
|
sscanf(line, " a = control: %*s");
|
|
tmpBuff = strstr(line, "track");
|
|
if(tmpBuff!=NULL){
|
|
if ((unsigned) countT < sizeof tracks / sizeof tracks[0]) {
|
|
//debug_msg("track = %s\n",tmpBuff);
|
|
strncpy(tracks[countT],tmpBuff,MIN(strlen(tmpBuff)-2, sizeof tracks[countT] - 1));
|
|
tracks[countT][MIN(strlen(tmpBuff)-2, sizeof tracks[countT] - 1)] = '\0';
|
|
countT++;
|
|
} else {
|
|
log_msg(LOG_LEVEL_WARNING, "skipping track = %s\n",tmpBuff);
|
|
}
|
|
}
|
|
tmpBuff=NULL;
|
|
int pt = 0;
|
|
sscanf(line, " a=rtpmap:%d %*s", &pt);
|
|
tmpBuff = strstr(line, "H264");
|
|
if(tmpBuff!=NULL){
|
|
if ((unsigned) countC < sizeof codecs / sizeof codecs[0]) {
|
|
//debug_msg("codec = %s\n",tmpBuff);
|
|
strncpy(codecs[countC],tmpBuff,4);
|
|
codecs[countC][4] = '\0';
|
|
countC++;
|
|
if (pt == 0) {
|
|
log_msg(LOG_LEVEL_ERROR, MOD_NAME "Missing video PT for H.264!\n");
|
|
return false;
|
|
}
|
|
rtspState->vrtsp_state.pt = pt;
|
|
} else {
|
|
log_msg(LOG_LEVEL_WARNING, "skipping codec = %s\n",tmpBuff);
|
|
}
|
|
}
|
|
tmpBuff=NULL;
|
|
sscanf(line, " a=rtpmap:97 %*s");
|
|
tmpBuff = strstr(line, "PCMU");
|
|
if(tmpBuff!=NULL){
|
|
if ((unsigned) countC < sizeof codecs / sizeof codecs[0]) {
|
|
//debug_msg("codec = %s\n",tmpBuff);
|
|
strncpy(codecs[countC],tmpBuff,4);
|
|
codecs[countC][4] = '\0';
|
|
countC++;
|
|
} else {
|
|
log_msg(LOG_LEVEL_WARNING, "skipping codec = %s\n",tmpBuff);
|
|
}
|
|
}
|
|
tmpBuff=NULL;
|
|
|
|
if(countT > 1 && countC > 1) break;
|
|
}
|
|
debug_msg("\nTRACK = %s FOR CODEC = %s",tracks[0],codecs[0]);
|
|
debug_msg("\nTRACK = %s FOR CODEC = %s\n",tracks[1],codecs[1]);
|
|
|
|
for(int p=0;p<2;p++){
|
|
if(strncmp(codecs[p],"H264",4)==0){
|
|
rtspState->vrtsp_state.codec = "H264";
|
|
rtspState->vrtsp_state.control = strdup(tracks[p]);
|
|
|
|
}if(strncmp(codecs[p],"PCMU",4)==0){
|
|
rtspState->artsp_state.codec = "PCMU";
|
|
rtspState->artsp_state.control = strdup(tracks[p]);
|
|
}
|
|
}
|
|
free(line);
|
|
rewind(sdp_file);
|
|
return true;
|
|
}
|
|
|
|
void getNewLine(const char* buffer, int* i, char* line){
|
|
int j=0;
|
|
while(buffer[*i] != '\n' && buffer[*i] != '\0'){
|
|
j++;
|
|
(*i)++;
|
|
}
|
|
if(buffer[*i] == '\n'){
|
|
j++;
|
|
(*i)++;
|
|
}
|
|
|
|
if(j>0){
|
|
memcpy(line,buffer+(*i)-j,j);
|
|
if (line[j - 1] == '\r') {
|
|
line[j - 1] = '\0';
|
|
}
|
|
}
|
|
line[j] = '\0';
|
|
}
|
|
/**
|
|
* Initializes decompressor if required by decompress flag
|
|
*/
|
|
static int
|
|
init_decompressor(struct video_rtsp_state *sr, struct video_desc desc) {
|
|
if (decompress_init_multi(H264, VIDEO_CODEC_NONE, UYVY, &sr->sd, 1)) {
|
|
decompress_reconfigure(sr->sd, desc, 16, 8, 0,
|
|
vc_get_linesize(desc.width, UYVY), UYVY);
|
|
} else
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* send RTSP GET PARAMS request
|
|
*/
|
|
static int
|
|
rtsp_get_parameters(CURL *curl, const char *uri) {
|
|
CURLcode res = CURLE_OK;
|
|
my_curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, uri, return -1);
|
|
my_curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST,
|
|
(long )CURL_RTSPREQ_GET_PARAMETER, return -1);
|
|
if ((res = curl_easy_perform(curl)) != CURLE_OK){
|
|
curl_print_error("[RTSP GET PARAMETERS] curl_easy_perform failed", res);
|
|
error_msg("[RTSP GET PARAMETERS] could not configure rtsp capture properly, \n\t\tplease check your parameters. \ncleaning...\n\n");
|
|
return 0;
|
|
}else{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* send RTSP OPTIONS request
|
|
*/
|
|
static int
|
|
rtsp_options(CURL *curl, const char *uri) {
|
|
char control[1500] = "",
|
|
user[1500] = "",
|
|
pass[1500] = "",
|
|
*strtoken;
|
|
|
|
CURLcode res = CURLE_OK;
|
|
debug_msg("\n[rtsp] OPTIONS %s\n", uri);
|
|
my_curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, uri, return -1);
|
|
|
|
sscanf(uri, "rtsp://%1500s", control);
|
|
strtoken = strtok(control, ":");
|
|
assert(strtoken != NULL);
|
|
strncpy(user, strtoken, sizeof user - 1);
|
|
strtoken = strtok(NULL, "@");
|
|
if (strtoken != NULL) {
|
|
strncpy(pass, strtoken, sizeof pass - 1);
|
|
my_curl_easy_setopt(curl, CURLOPT_USERNAME, user, return -1);
|
|
my_curl_easy_setopt(curl, CURLOPT_PASSWORD, pass, return -1);
|
|
}
|
|
|
|
my_curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, (long )CURL_RTSPREQ_OPTIONS, return -1);
|
|
|
|
if ((res = curl_easy_perform(curl)) != CURLE_OK){
|
|
curl_print_error("[RTSP OPTIONS] curl_easy_perform failed", res);
|
|
error_msg("[RTSP OPTIONS] could not configure rtsp capture properly, \n\t\tplease check your parameters. \ncleaning...\n\n");
|
|
return 0;
|
|
}else{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* send RTSP DESCRIBE request and write sdp response to a file
|
|
*/
|
|
static bool
|
|
rtsp_describe(CURL *curl, const char *uri, FILE *sdp_fp) {
|
|
CURLcode res = CURLE_OK;
|
|
debug_msg("\n[rtsp] DESCRIBE %s\n", uri);
|
|
my_curl_easy_setopt(curl, CURLOPT_WRITEDATA, sdp_fp, goto error);
|
|
my_curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST,
|
|
(long )CURL_RTSPREQ_DESCRIBE, goto error);
|
|
|
|
if ((res = curl_easy_perform(curl)) != CURLE_OK){
|
|
curl_print_error("[RTSP DESCRIGE] curl_easy_perform failed", res);
|
|
error_msg("[RTSP DESCRIBE] could not configure rtsp capture properly, \n\t\tplease check your parameters. \ncleaning...\n\n");
|
|
goto error;
|
|
}
|
|
|
|
my_curl_easy_setopt(curl, CURLOPT_WRITEDATA, stdout, goto error);
|
|
rewind(sdp_fp);
|
|
return true;
|
|
error:
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* send RTSP SETUP request
|
|
*/
|
|
static int
|
|
rtsp_setup(CURL *curl, const char *uri, const char *transport) {
|
|
CURLcode res = CURLE_OK;
|
|
debug_msg("\n[rtsp] SETUP %s\n", uri);
|
|
debug_msg("\t TRANSPORT %s\n", transport);
|
|
my_curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, uri, return -1);
|
|
my_curl_easy_setopt(curl, CURLOPT_RTSP_TRANSPORT, transport, return -1);
|
|
my_curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, (long )CURL_RTSPREQ_SETUP, return -1);
|
|
|
|
if ((res = curl_easy_perform(curl)) != CURLE_OK){
|
|
curl_print_error("[RTSP SETUP] curl_easy_perform failed", res);
|
|
error_msg("[RTSP SETUP] could not configure rtsp capture properly, \n\t\tplease check your parameters. \ncleaning...\n\n");
|
|
return 0;
|
|
}else{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* send RTSP PLAY request
|
|
*/
|
|
static int
|
|
rtsp_play(CURL *curl, const char *uri, const char * /* range */) {
|
|
CURLcode res = CURLE_OK;
|
|
debug_msg("\n[rtsp] PLAY %s\n", uri);
|
|
my_curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, uri, return -1);
|
|
//my_curl_easy_setopt(curl, CURLOPT_RANGE, range); //range not set because we want (right now) no limit range for streaming duration
|
|
my_curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, (long )CURL_RTSPREQ_PLAY, return -1);
|
|
|
|
if ((res = curl_easy_perform(curl)) != CURLE_OK){
|
|
curl_print_error("[RTSP PLAY] curl_easy_perform failed", res);
|
|
error_msg("[RTSP PLAY] could not configure rtsp capture properly, \n\t\tplease check your parameters. \ncleaning...\n\n");
|
|
return 0;
|
|
}else{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* send RTSP TEARDOWN request
|
|
*/
|
|
static int
|
|
rtsp_teardown(CURL *curl, const char *uri) {
|
|
CURLcode res = CURLE_OK;
|
|
debug_msg("\n[rtsp] TEARDOWN %s\n", uri);
|
|
my_curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST,
|
|
(long )CURL_RTSPREQ_TEARDOWN, return -1);
|
|
|
|
if ((res = curl_easy_perform(curl)) != CURLE_OK){
|
|
curl_print_error("[RTSP TEAR DOWN] curl_easy_perform failed", res);
|
|
error_msg("[RTSP TEARD DOWN] could not configure rtsp capture properly, \n\t\tplease check your parameters. \ncleaning...\n\n");
|
|
return 0;
|
|
}else{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static struct vidcap_type *
|
|
vidcap_rtsp_probe(bool verbose, void (**deleter)(void *)) {
|
|
UNUSED(verbose);
|
|
*deleter = free;
|
|
struct vidcap_type *vt;
|
|
|
|
vt = (struct vidcap_type *) calloc(1, sizeof(struct vidcap_type));
|
|
if (vt != NULL) {
|
|
vt->name = "rtsp";
|
|
vt->description = "Video capture from RTSP remote server";
|
|
}
|
|
return vt;
|
|
}
|
|
|
|
static void
|
|
vidcap_rtsp_done(void *state) {
|
|
struct rtsp_state *s = (struct rtsp_state *) state;
|
|
|
|
pthread_mutex_lock(&s->lock);
|
|
s->should_exit = TRUE;
|
|
pthread_cond_signal(&s->keepalive_cv);
|
|
pthread_mutex_unlock(&s->lock);
|
|
|
|
if (s->vrtsp_state.vrtsp_thread_id) {
|
|
pthread_join(s->vrtsp_state.vrtsp_thread_id, NULL);
|
|
}
|
|
if (s->keep_alive_rtsp_thread_id) {
|
|
pthread_join(s->keep_alive_rtsp_thread_id, NULL);
|
|
}
|
|
|
|
if(s->vrtsp_state.sd)
|
|
decompress_done(s->vrtsp_state.sd);
|
|
|
|
if (s->vrtsp_state.device != nullptr) {
|
|
rtp_done(s->vrtsp_state.device);
|
|
}
|
|
|
|
if(s->vrtsp_state.h264_offset_buffer!=NULL) free(s->vrtsp_state.h264_offset_buffer);
|
|
vf_free(s->vrtsp_state.out_frame);
|
|
free(s->vrtsp_state.control);
|
|
free(s->artsp_state.control);
|
|
|
|
rtsp_teardown(s->curl, s->uri);
|
|
|
|
curl_easy_cleanup(s->curl);
|
|
curl_global_cleanup();
|
|
s->curl = NULL;
|
|
|
|
pthread_mutex_destroy(&s->lock);
|
|
pthread_cond_destroy(&s->keepalive_cv);
|
|
pthread_mutex_destroy(&s->vrtsp_state.lock);
|
|
pthread_cond_destroy(&s->vrtsp_state.boss_cv);
|
|
pthread_cond_destroy(&s->vrtsp_state.worker_cv);
|
|
|
|
free(s);
|
|
}
|
|
|
|
/**
|
|
* scan sdp file for media control attributes to generate coded frame required params (WxH and offset)
|
|
*/
|
|
static int
|
|
get_nals(FILE *sdp_file, char *nals, int *width, int *height) {
|
|
|
|
uint8_t nalInfo;
|
|
uint8_t type;
|
|
uint8_t nri __attribute__((unused));
|
|
int max_len = 1500, len_nals = 0;
|
|
char *s = (char *) malloc(max_len);
|
|
char *sprop;
|
|
memset(s, 0, max_len);
|
|
nals[0] = '\0';
|
|
|
|
while (fgets(s, max_len - 2, sdp_file) != NULL) {
|
|
sprop = strstr(s, "sprop-parameter-sets=");
|
|
if (sprop != NULL) {
|
|
gsize length; //gsize is an unsigned int.
|
|
char *nal_aux, *nal;
|
|
memcpy(nals, start_sequence, sizeof(start_sequence));
|
|
len_nals = sizeof(start_sequence);
|
|
nal_aux = strstr(sprop, "=");
|
|
nal_aux++;
|
|
nal = strtok(nal_aux, ",;");
|
|
if (nal == nullptr) {
|
|
continue;
|
|
}
|
|
//convert base64 to hex
|
|
guchar *nals_aux = g_base64_decode(nal, &length);
|
|
memcpy(nals + len_nals, nals_aux, length);
|
|
g_free(nals_aux);
|
|
len_nals += length;
|
|
|
|
nalInfo = (uint8_t) nals[4];
|
|
type = nalInfo & 0x1f;
|
|
nri = nalInfo & 0x60;
|
|
|
|
if (type == 7){
|
|
width_height_from_SDP(width, height , (unsigned char *) (nals+4), length);
|
|
}
|
|
|
|
while ((nal = strtok(NULL, ",;")) != NULL) {
|
|
guchar *nals_aux = g_base64_decode(nal, &length);
|
|
if (length) {
|
|
//convert base64 to hex
|
|
memcpy(nals+len_nals, start_sequence, sizeof(start_sequence));
|
|
len_nals += sizeof(start_sequence);
|
|
memcpy(nals + len_nals, nals_aux, length);
|
|
len_nals += length;
|
|
|
|
nalInfo = (uint8_t) nals[len_nals - length];
|
|
type = nalInfo & 0x1f;
|
|
nri = nalInfo & 0x60;
|
|
|
|
if (type == 7){
|
|
width_height_from_SDP(width, height , (unsigned char *) (nals+(len_nals - length)), length);
|
|
}
|
|
//assure start sequence injection between sps, pps and other nals
|
|
memcpy(nals+len_nals, start_sequence, sizeof(start_sequence));
|
|
len_nals += sizeof(start_sequence);
|
|
}
|
|
g_free(nals_aux);
|
|
}
|
|
}
|
|
}
|
|
|
|
free(s);
|
|
rewind(sdp_file);
|
|
return len_nals;
|
|
}
|
|
|
|
static const struct video_capture_info vidcap_rtsp_info = {
|
|
vidcap_rtsp_probe,
|
|
vidcap_rtsp_init,
|
|
vidcap_rtsp_done,
|
|
vidcap_rtsp_grab,
|
|
true
|
|
};
|
|
|
|
REGISTER_MODULE(rtsp, &vidcap_rtsp_info, LIBRARY_CLASS_VIDEO_CAPTURE, VIDEO_CAPTURE_ABI_VERSION);
|
|
|
|
/* vim: set expandtab sw=4: */
|