mirror of
https://github.com/outbackdingo/UltraGrid.git
synced 2026-03-21 20:40:27 +00:00
Reworked video sending/receiving workflow
Moved network related stuff to separated files.
This commit is contained in:
@@ -145,7 +145,10 @@ OBJS = @OBJS@ \
|
||||
ldgm-coding/matrix-gen/matrix-generator.o \
|
||||
ldgm-coding/matrix-gen/ldpc-matrix.o \
|
||||
|
||||
ULTRAGRID_OBJS = src/main.o src/sender.o \
|
||||
ULTRAGRID_OBJS = src/main.o \
|
||||
src/video_rxtx.o \
|
||||
src/video_rxtx/rtp.o \
|
||||
src/video_rxtx/ultragrid_rtp.o
|
||||
|
||||
REFLECTOR_OBJS = src/hd-rum-translator/hd-rum-decompress.o \
|
||||
src/hd-rum-translator/hd-rum-recompress.o \
|
||||
|
||||
114
src/host.c
114
src/host.c
@@ -14,8 +14,10 @@
|
||||
#include "video_display.h"
|
||||
|
||||
#include "utils/resource_manager.h"
|
||||
#include "rtp/video_decoders.h"
|
||||
#include "pdb.h"
|
||||
#include "rtp/rtp.h"
|
||||
#include "rtp/rtp_callback.h"
|
||||
#include "rtp/video_decoders.h"
|
||||
#include "rtp/pbuf.h"
|
||||
|
||||
long long bitrate = 0;
|
||||
@@ -37,6 +39,8 @@ volatile bool should_exit_receiver = false;
|
||||
|
||||
bool verbose = false;
|
||||
|
||||
int rxtx_mode; // MODE_SENDER, MODE_RECEIVER or both
|
||||
|
||||
int initialize_video_capture(struct module *parent,
|
||||
const struct vidcap_params *params,
|
||||
struct vidcap **state)
|
||||
@@ -112,3 +116,111 @@ int initialize_video_display(const char *requested_display,
|
||||
return display_init(id, fmt, flags, out);
|
||||
}
|
||||
|
||||
void display_buf_increase_warning(int size)
|
||||
{
|
||||
fprintf(stderr, "\n***\n"
|
||||
"Unable to set buffer size to %d B.\n"
|
||||
"Please set net.core.rmem_max value to %d or greater. (see also\n"
|
||||
"https://www.sitola.cz/igrid/index.php/Setup_UltraGrid)\n"
|
||||
#ifdef HAVE_MACOSX
|
||||
"\tsysctl -w kern.ipc.maxsockbuf=%d\n"
|
||||
"\tsysctl -w net.inet.udp.recvspace=%d\n"
|
||||
#else
|
||||
"\tsysctl -w net.core.rmem_max=%d\n"
|
||||
#endif
|
||||
"To make this persistent, add these options (key=value) to /etc/sysctl.conf\n"
|
||||
"\n***\n\n",
|
||||
size, size,
|
||||
#ifdef HAVE_MACOSX
|
||||
size * 4,
|
||||
#endif /* HAVE_MACOSX */
|
||||
size);
|
||||
|
||||
}
|
||||
|
||||
struct rtp **initialize_network(const char *addrs, int recv_port_base,
|
||||
int send_port_base, struct pdb *participants, bool use_ipv6,
|
||||
const char *mcast_if)
|
||||
{
|
||||
struct rtp **devices = NULL;
|
||||
double rtcp_bw = 5 * 1024 * 1024; /* FIXME */
|
||||
int ttl = 255;
|
||||
char *saveptr = NULL;
|
||||
char *addr;
|
||||
char *tmp;
|
||||
int required_connections, index;
|
||||
int recv_port = recv_port_base;
|
||||
int send_port = send_port_base;
|
||||
|
||||
tmp = strdup(addrs);
|
||||
if(strtok_r(tmp, ",", &saveptr) == NULL) {
|
||||
free(tmp);
|
||||
return NULL;
|
||||
}
|
||||
else required_connections = 1;
|
||||
while(strtok_r(NULL, ",", &saveptr) != NULL)
|
||||
++required_connections;
|
||||
|
||||
free(tmp);
|
||||
tmp = strdup(addrs);
|
||||
|
||||
devices = (struct rtp **)
|
||||
malloc((required_connections + 1) * sizeof(struct rtp *));
|
||||
|
||||
for(index = 0, addr = strtok_r(tmp, ",", &saveptr);
|
||||
index < required_connections;
|
||||
++index, addr = strtok_r(NULL, ",", &saveptr), recv_port += 2, send_port += 2)
|
||||
{
|
||||
/* port + 2 is reserved for audio */
|
||||
if (recv_port == recv_port_base + 2)
|
||||
recv_port += 2;
|
||||
if (send_port == send_port_base + 2)
|
||||
send_port += 2;
|
||||
|
||||
devices[index] = rtp_init_if(addr, mcast_if, recv_port,
|
||||
send_port, ttl, rtcp_bw, FALSE,
|
||||
rtp_recv_callback, (uint8_t *)participants,
|
||||
use_ipv6);
|
||||
if (devices[index] != NULL) {
|
||||
rtp_set_option(devices[index], RTP_OPT_WEAK_VALIDATION,
|
||||
TRUE);
|
||||
rtp_set_sdes(devices[index], rtp_my_ssrc(devices[index]),
|
||||
RTCP_SDES_TOOL,
|
||||
PACKAGE_STRING, strlen(PACKAGE_STRING));
|
||||
|
||||
int size = INITIAL_VIDEO_RECV_BUFFER_SIZE;
|
||||
int ret = rtp_set_recv_buf(devices[index], INITIAL_VIDEO_RECV_BUFFER_SIZE);
|
||||
if(!ret) {
|
||||
display_buf_increase_warning(size);
|
||||
}
|
||||
|
||||
rtp_set_send_buf(devices[index], 1024 * 56);
|
||||
|
||||
pdb_add(participants, rtp_my_ssrc(devices[index]));
|
||||
}
|
||||
else {
|
||||
int index_nest;
|
||||
for(index_nest = 0; index_nest < index; ++index_nest) {
|
||||
rtp_done(devices[index_nest]);
|
||||
}
|
||||
free(devices);
|
||||
devices = NULL;
|
||||
}
|
||||
}
|
||||
if(devices != NULL) devices[index] = NULL;
|
||||
free(tmp);
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
void destroy_rtp_devices(struct rtp ** network_devices)
|
||||
{
|
||||
struct rtp ** current = network_devices;
|
||||
if(!network_devices)
|
||||
return;
|
||||
while(*current != NULL) {
|
||||
rtp_done(*current++);
|
||||
}
|
||||
free(network_devices);
|
||||
}
|
||||
|
||||
|
||||
17
src/host.h
17
src/host.h
@@ -57,6 +57,13 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MACOSX
|
||||
#define INITIAL_VIDEO_RECV_BUFFER_SIZE 5944320
|
||||
#else
|
||||
#define INITIAL_VIDEO_RECV_BUFFER_SIZE ((4*1920*1080)*110/100)
|
||||
#endif
|
||||
|
||||
struct pdb;
|
||||
struct rtp;
|
||||
struct state_uv;
|
||||
struct video_frame;
|
||||
@@ -92,6 +99,10 @@ extern const char *sage_receiver;
|
||||
|
||||
extern bool verbose;
|
||||
|
||||
#define MODE_SENDER 1
|
||||
#define MODE_RECEIVER 2
|
||||
extern int rxtx_mode;
|
||||
|
||||
// for aggregate.c
|
||||
struct vidcap;
|
||||
struct display;
|
||||
@@ -104,9 +115,15 @@ int initialize_video_capture(struct module *parent,
|
||||
const struct vidcap_params *params,
|
||||
struct vidcap **);
|
||||
|
||||
struct rtp **initialize_network(const char *addrs, int recv_port_base,
|
||||
int send_port_base, struct pdb *participants, bool use_ipv6,
|
||||
const char *mcast_if);
|
||||
|
||||
void *ultragrid_rtp_receiver_thread(void *arg);
|
||||
void destroy_rtp_devices(struct rtp ** network_devices);
|
||||
struct rtp **change_tx_port(struct state_uv *, int port);
|
||||
void display_buf_increase_warning(int size);
|
||||
|
||||
|
||||
// if not NULL, data should be exported
|
||||
extern char *export_dir;
|
||||
|
||||
@@ -61,9 +61,9 @@
|
||||
#include "host.h"
|
||||
#include "ihdtv.h"
|
||||
#include "ihdtv/ihdtv.h"
|
||||
#include "sender.h"
|
||||
#include "video_display.h"
|
||||
#include "video_capture.h"
|
||||
#include "video_rxtx.h"
|
||||
|
||||
struct ihdtv_state {
|
||||
#ifdef HAVE_IHDTV
|
||||
@@ -212,7 +212,7 @@ struct ihdtv_state *initialize_ihdtv(struct vidcap *capture_device, struct displ
|
||||
|
||||
static void ihdtv_done(void *state)
|
||||
{
|
||||
struct ihdtv_state *s = state;
|
||||
struct ihdtv_state *s = (struct ihdtv_state *) state;
|
||||
if(!s)
|
||||
return;
|
||||
free(s);
|
||||
740
src/main.cpp
740
src/main.cpp
File diff suppressed because it is too large
Load Diff
314
src/sender.c
314
src/sender.c
@@ -1,314 +0,0 @@
|
||||
/*
|
||||
* FILE: sender.c
|
||||
* AUTHORS: Colin Perkins <csp@csperkins.org>
|
||||
* Ladan Gharai <ladan@isi.edu>
|
||||
* Martin Benes <martinbenesh@gmail.com>
|
||||
* Lukas Hejtmanek <xhejtman@ics.muni.cz>
|
||||
* Petr Holub <hopet@ics.muni.cz>
|
||||
* Milos Liska <xliska@fi.muni.cz>
|
||||
* Jiri Matela <matela@ics.muni.cz>
|
||||
* Dalibor Matura <255899@mail.muni.cz>
|
||||
* Ian Wesley-Smith <iwsmith@cct.lsu.edu>
|
||||
* David Cassany <david.cassany@i2cat.net>
|
||||
* Ignacio Contreras <ignacio.contreras@i2cat.net>
|
||||
* Gerard Castillo <gerard.castillo@i2cat.net>
|
||||
*
|
||||
* Copyright (c) 2005-2010 Fundació i2CAT, Internet I Innovació Digital a Catalunya
|
||||
* Copyright (c) 2005-2010 CESNET z.s.p.o.
|
||||
* Copyright (c) 2001-2004 University of Southern California
|
||||
* Copyright (c) 2003-2004 University of Glasgow
|
||||
*
|
||||
* 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. This product also includes
|
||||
* software developed by CESNET z.s.p.o.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#include "config_unix.h"
|
||||
#include "config_win32.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#include "host.h"
|
||||
#include "ihdtv.h"
|
||||
#include "messaging.h"
|
||||
#include "module.h"
|
||||
#include "rtp/rtp.h"
|
||||
#include "sender.h"
|
||||
#include "stats.h"
|
||||
#include "transmit.h"
|
||||
#include "utils/vf_split.h"
|
||||
#include "video.h"
|
||||
#include "video_compress.h"
|
||||
#include "video_display.h"
|
||||
#include "video_export.h"
|
||||
|
||||
static void *sender_thread(void *arg);
|
||||
static void ultragrid_rtp_send(void *state, struct video_frame *tx_frame);
|
||||
static void ultragrid_rtp_done(void *state);
|
||||
static void sage_rxtx_send(void *state, struct video_frame *tx_frame);
|
||||
static void sage_rxtx_done(void *state);
|
||||
static void h264_rtp_send(void *state, struct video_frame *tx_frame);
|
||||
static void h264_rtp_done(void *state);
|
||||
|
||||
struct sender_priv_data {
|
||||
struct module mod;
|
||||
|
||||
pthread_mutex_t lock;
|
||||
|
||||
pthread_t thread_id;
|
||||
bool paused;
|
||||
};
|
||||
|
||||
struct rx_tx ultragrid_rtp = {
|
||||
ULTRAGRID_RTP,
|
||||
"UltraGrid RTP",
|
||||
ultragrid_rtp_send,
|
||||
ultragrid_rtp_done,
|
||||
ultragrid_rtp_receiver_thread
|
||||
};
|
||||
|
||||
struct rx_tx sage_rxtx = {
|
||||
SAGE,
|
||||
"SAGE",
|
||||
sage_rxtx_send,
|
||||
sage_rxtx_done,
|
||||
NULL
|
||||
};
|
||||
|
||||
struct rx_tx h264_rtp = {
|
||||
H264_STD,
|
||||
"H264 standard",
|
||||
h264_rtp_send,
|
||||
h264_rtp_done,
|
||||
NULL //TODO: h264_rtp_receiver_thread
|
||||
};
|
||||
|
||||
static void sender_process_external_message(struct sender_data *data, struct msg_sender *msg)
|
||||
{
|
||||
int ret;
|
||||
switch(msg->type) {
|
||||
case SENDER_MSG_CHANGE_RECEIVER:
|
||||
assert(data->rxtx_protocol == ULTRAGRID_RTP || data->rxtx_protocol == H264_STD);
|
||||
assert(((struct ultragrid_rtp_state *) data->tx_module_state)->connections_count == 1);
|
||||
|
||||
if(data->rxtx_protocol == ULTRAGRID_RTP){
|
||||
ret = rtp_change_dest(((struct ultragrid_rtp_state *)
|
||||
data->tx_module_state)->network_devices[0],
|
||||
msg->receiver);
|
||||
} else { // if(data->rxtx_protocol == H264_STD) {
|
||||
ret = rtp_change_dest(
|
||||
((struct h264_rtp_state *) data->tx_module_state)->network_devices[0],
|
||||
msg->receiver);
|
||||
}
|
||||
|
||||
if(ret == FALSE) {
|
||||
fprintf(stderr, "Changing receiver to: %s failed!\n",
|
||||
msg->receiver);
|
||||
}
|
||||
break;
|
||||
case SENDER_MSG_CHANGE_PORT:
|
||||
((struct ultragrid_rtp_state *)
|
||||
data->tx_module_state)->network_devices
|
||||
= change_tx_port(data->uv, msg->port);
|
||||
break;
|
||||
case SENDER_MSG_PAUSE:
|
||||
data->priv->paused = true;
|
||||
break;
|
||||
case SENDER_MSG_PLAY:
|
||||
data->priv->paused = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool sender_init(struct sender_data *data) {
|
||||
data->priv = calloc(1, sizeof(struct sender_priv_data));
|
||||
pthread_mutex_init(&data->priv->lock, NULL);
|
||||
|
||||
if (pthread_create
|
||||
(&data->priv->thread_id, NULL, sender_thread,
|
||||
(void *) data) != 0) {
|
||||
perror("Unable to create sender thread!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void sender_done(struct sender_data *data) {
|
||||
pthread_join(data->priv->thread_id, NULL);
|
||||
|
||||
free(data->priv);
|
||||
}
|
||||
|
||||
static void ultragrid_rtp_send(void *state, struct video_frame *tx_frame)
|
||||
{
|
||||
struct ultragrid_rtp_state *data = (struct ultragrid_rtp_state *) state;
|
||||
|
||||
if(data->connections_count == 1) { /* normal case - only one connection */
|
||||
tx_send(data->tx, tx_frame,
|
||||
data->network_devices[0]);
|
||||
} else { /* split */
|
||||
struct video_frame *split_frames = vf_alloc(data->connections_count);
|
||||
|
||||
//assert(frame_count == 1);
|
||||
vf_split_horizontal(split_frames, tx_frame,
|
||||
data->connections_count);
|
||||
for (int i = 0; i < data->connections_count; ++i) {
|
||||
tx_send_tile(data->tx, split_frames, i,
|
||||
data->network_devices[i]);
|
||||
}
|
||||
|
||||
vf_free(split_frames);
|
||||
}
|
||||
}
|
||||
|
||||
static void ultragrid_rtp_done(void *state)
|
||||
{
|
||||
struct ultragrid_rtp_state *data = (struct ultragrid_rtp_state *) state;
|
||||
|
||||
if (data->tx) {
|
||||
module_done(CAST_MODULE(data->tx));
|
||||
}
|
||||
}
|
||||
|
||||
static void sage_rxtx_send(void *state, struct video_frame *tx_frame)
|
||||
{
|
||||
struct sage_rxtx_state *data = (struct sage_rxtx_state *) state;
|
||||
|
||||
if(!video_desc_eq(data->saved_vid_desc,
|
||||
video_desc_from_frame(tx_frame))) {
|
||||
display_reconfigure(data->sage_tx_device,
|
||||
video_desc_from_frame(tx_frame));
|
||||
data->saved_vid_desc = video_desc_from_frame(tx_frame);
|
||||
}
|
||||
struct video_frame *frame =
|
||||
display_get_frame(data->sage_tx_device);
|
||||
memcpy(frame->tiles[0].data, tx_frame->tiles[0].data,
|
||||
tx_frame->tiles[0].data_len);
|
||||
display_put_frame(data->sage_tx_device, frame, PUTF_NONBLOCK);
|
||||
|
||||
VIDEO_FRAME_DISPOSE(tx_frame);
|
||||
}
|
||||
|
||||
static void sage_rxtx_done(void *state)
|
||||
{
|
||||
struct sage_rxtx_state *data = (struct sage_rxtx_state *) state;
|
||||
|
||||
// poisoned pill to exit thread
|
||||
display_put_frame(data->sage_tx_device, NULL, PUTF_NONBLOCK);
|
||||
pthread_join(data->thread_id, NULL);
|
||||
|
||||
display_done(data->sage_tx_device);
|
||||
}
|
||||
|
||||
static void h264_rtp_send(void *state, struct video_frame *tx_frame)
|
||||
{
|
||||
struct h264_rtp_state *data = (struct h264_rtp_state *) state;
|
||||
|
||||
if(data->connections_count == 1) { /* normal/default case - only one connection */
|
||||
tx_send_h264(data->tx, tx_frame, data->network_devices[0]);
|
||||
} else {
|
||||
//TODO to be tested, the idea is to reply per destiny
|
||||
for (int i = 0; i < data->connections_count; ++i) {
|
||||
tx_send_h264(data->tx, tx_frame,
|
||||
data->network_devices[i]);
|
||||
}
|
||||
}
|
||||
|
||||
VIDEO_FRAME_DISPOSE(tx_frame);
|
||||
}
|
||||
|
||||
static void h264_rtp_done(void *state)
|
||||
{
|
||||
struct h264_rtp_state *data = (struct h264_rtp_state *) state;
|
||||
|
||||
if (data->tx) {
|
||||
module_done(CAST_MODULE(data->tx));
|
||||
}
|
||||
}
|
||||
|
||||
static void *sender_thread(void *arg) {
|
||||
struct sender_data *data = (struct sender_data *)arg;
|
||||
struct video_desc saved_vid_desc;
|
||||
|
||||
memset(&saved_vid_desc, 0, sizeof(saved_vid_desc));
|
||||
|
||||
module_init_default(&data->priv->mod);
|
||||
data->priv->mod.cls = MODULE_CLASS_SENDER;
|
||||
data->priv->mod.priv_data = data;
|
||||
module_register(&data->priv->mod, data->parent);
|
||||
|
||||
struct module *control_mod = get_module(get_root_module(&data->priv->mod), "control");
|
||||
struct stats *stat_data_sent = stats_new_statistics((struct control_state *)
|
||||
control_mod, "data");
|
||||
|
||||
while(1) {
|
||||
// process external messages
|
||||
struct message *msg_external;
|
||||
while((msg_external = check_message(&data->priv->mod))) {
|
||||
sender_process_external_message(data, (struct msg_sender *) msg_external);
|
||||
free_message(msg_external);
|
||||
}
|
||||
|
||||
struct video_frame *tx_frame = NULL;
|
||||
|
||||
tx_frame = compress_pop(data->compression);
|
||||
if (!tx_frame)
|
||||
goto exit;
|
||||
|
||||
video_export(data->video_exporter, tx_frame);
|
||||
|
||||
if (!data->priv->paused) {
|
||||
data->send_frame(data->tx_module_state, tx_frame);
|
||||
}
|
||||
|
||||
VIDEO_FRAME_DISPOSE(tx_frame);
|
||||
|
||||
if (data->rxtx_protocol == ULTRAGRID_RTP || data->rxtx_protocol == H264_STD) {
|
||||
struct ultragrid_rtp_state *rtp_state = data->tx_module_state;
|
||||
stats_update_int(stat_data_sent,
|
||||
rtp_get_bytes_sent(rtp_state->network_devices[0]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
exit:
|
||||
module_done(&data->priv->mod);
|
||||
stats_destroy(stat_data_sent);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
140
src/sender.h
140
src/sender.h
@@ -1,140 +0,0 @@
|
||||
/*
|
||||
* FILE: main.c
|
||||
* AUTHORS: Colin Perkins <csp@csperkins.org>
|
||||
* Ladan Gharai <ladan@isi.edu>
|
||||
* Martin Benes <martinbenesh@gmail.com>
|
||||
* Lukas Hejtmanek <xhejtman@ics.muni.cz>
|
||||
* Petr Holub <hopet@ics.muni.cz>
|
||||
* Milos Liska <xliska@fi.muni.cz>
|
||||
* Jiri Matela <matela@ics.muni.cz>
|
||||
* Dalibor Matura <255899@mail.muni.cz>
|
||||
* Ian Wesley-Smith <iwsmith@cct.lsu.edu>
|
||||
* David Cassany <david.cassany@i2cat.net>
|
||||
* Ignacio Contreras <ignacio.contreras@i2cat.net>
|
||||
* Gerard Castillo <gerard.castillo@i2cat.net>
|
||||
*
|
||||
* Copyright (c) 2005-2010 Fundació i2CAT, Internet I Innovació Digital a Catalunya
|
||||
* Copyright (c) 2005-2010 CESNET z.s.p.o.
|
||||
* Copyright (c) 2001-2004 University of Southern California
|
||||
* Copyright (c) 2003-2004 University of Glasgow
|
||||
*
|
||||
* 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. This product also includes
|
||||
* software developed by CESNET z.s.p.o.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SENDER_H_
|
||||
#define SENDER_H_
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#include "config_unix.h"
|
||||
#include "config_win32.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#include "video.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct tx;
|
||||
struct rtp;
|
||||
struct display;
|
||||
struct ihdtv_state;
|
||||
struct module;
|
||||
struct received_message;
|
||||
struct response;
|
||||
struct sender_msg;
|
||||
struct sender_priv_data;
|
||||
struct video_compress;
|
||||
|
||||
enum rxtx_protocol {
|
||||
ULTRAGRID_RTP,
|
||||
IHDTV,
|
||||
SAGE,
|
||||
H264_STD
|
||||
};
|
||||
|
||||
struct rx_tx {
|
||||
enum rxtx_protocol protocol;
|
||||
const char *name;
|
||||
void (*send)(void *, struct video_frame *);
|
||||
void (*done)(void *);
|
||||
void *(*receiver_thread)(void *);
|
||||
};
|
||||
|
||||
struct sender_data {
|
||||
struct module *parent;
|
||||
enum rxtx_protocol rxtx_protocol;
|
||||
void (*send_frame)(void *state, struct video_frame *);
|
||||
void *tx_module_state;
|
||||
struct state_uv *uv;
|
||||
struct sender_priv_data *priv;
|
||||
struct video_export *video_exporter;
|
||||
struct compress_state *compression;
|
||||
};
|
||||
|
||||
extern struct rx_tx ultragrid_rtp;
|
||||
extern struct rx_tx sage_rxtx;
|
||||
extern struct rx_tx h264_rtp;
|
||||
|
||||
struct ultragrid_rtp_state {
|
||||
int connections_count;
|
||||
struct rtp **network_devices; // ULTRAGRID_RTP
|
||||
struct tx *tx;
|
||||
};
|
||||
|
||||
struct sage_rxtx_state {
|
||||
struct video_desc saved_vid_desc;
|
||||
struct display *sage_tx_device;
|
||||
pthread_t thread_id;
|
||||
};
|
||||
|
||||
struct h264_rtp_state {
|
||||
int connections_count;
|
||||
struct rtp **network_devices;
|
||||
struct tx *tx;
|
||||
};
|
||||
|
||||
bool sender_init(struct sender_data *data);
|
||||
void sender_done(struct sender_data *data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // SENDER_H_
|
||||
|
||||
@@ -206,7 +206,7 @@ static void tx_update(struct tx *tx, struct tile *tile)
|
||||
}
|
||||
|
||||
struct tx *tx_init(struct module *parent, unsigned mtu, enum tx_media_type media_type,
|
||||
char *fec, const char *encryption, long packet_rate)
|
||||
const char *fec, const char *encryption, long packet_rate)
|
||||
{
|
||||
struct tx *tx;
|
||||
|
||||
@@ -252,7 +252,7 @@ struct tx *tx_init(struct module *parent, unsigned mtu, enum tx_media_type media
|
||||
}
|
||||
|
||||
struct tx *tx_init_h264(struct module *parent, unsigned mtu, enum tx_media_type media_type,
|
||||
char *fec, const char *encryption, long packet_rate)
|
||||
const char *fec, const char *encryption, long packet_rate)
|
||||
{
|
||||
return tx_init(parent, mtu, media_type, fec, encryption, packet_rate);
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ struct tx;
|
||||
struct video_frame;
|
||||
|
||||
struct tx *tx_init(struct module *parent, unsigned mtu, enum tx_media_type media_type,
|
||||
char *fec, const char *encryption, long packet_rate);
|
||||
const char *fec, const char *encryption, long packet_rate);
|
||||
void tx_send_tile(struct tx *tx_session, struct video_frame *frame, int pos, struct rtp *rtp_session);
|
||||
void tx_send(struct tx *tx_session, struct video_frame *frame, struct rtp *rtp_session);
|
||||
void audio_tx_send(struct tx *tx_session, struct rtp *rtp_session, audio_frame2 *buffer);
|
||||
@@ -78,7 +78,7 @@ void audio_tx_send_mulaw(struct tx* tx, struct rtp *rtp_session, aud
|
||||
|
||||
|
||||
struct tx *tx_init_h264(struct module *parent, unsigned mtu, enum tx_media_type media_type,
|
||||
char *fec, const char *encryption, long packet_rate);
|
||||
const char *fec, const char *encryption, long packet_rate);
|
||||
|
||||
void tx_send_h264(struct tx *tx_session, struct video_frame *frame, struct rtp *rtp_session);
|
||||
|
||||
|
||||
251
src/video_rxtx.cpp
Normal file
251
src/video_rxtx.cpp
Normal file
@@ -0,0 +1,251 @@
|
||||
/**
|
||||
* @file video_rxtx.cpp
|
||||
* @author Martin Pulec <pulec@cesnet.cz>
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2013-2014 CESNET z.s.p.o.
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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. Neither the name of CESNET 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 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.
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#include "config_unix.h"
|
||||
#include "config_win32.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "host.h"
|
||||
#include "ihdtv.h"
|
||||
#include "messaging.h"
|
||||
#include "module.h"
|
||||
#include "pdb.h"
|
||||
#include "rtp/rtp.h"
|
||||
#include "rtp/video_decoders.h"
|
||||
#include "rtp/pbuf.h"
|
||||
#include "tfrc.h"
|
||||
#include "stats.h"
|
||||
#include "transmit.h"
|
||||
#include "tv.h"
|
||||
#include "utils/vf_split.h"
|
||||
#include "video.h"
|
||||
#include "video_compress.h"
|
||||
#include "video_decompress.h"
|
||||
#include "video_display.h"
|
||||
#include "video_export.h"
|
||||
#include "video_rxtx.h"
|
||||
#include "video_rxtx/rtp.h"
|
||||
#include "video_rxtx/ultragrid_rtp.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static void sage_rxtx_send(void *state, struct video_frame *tx_frame);
|
||||
static void sage_rxtx_done(void *state);
|
||||
static void h264_rtp_send(void *state, struct video_frame *tx_frame);
|
||||
static void h264_rtp_done(void *state);
|
||||
|
||||
struct rx_tx sage_rxtx = {
|
||||
SAGE,
|
||||
"SAGE",
|
||||
sage_rxtx_send,
|
||||
sage_rxtx_done,
|
||||
NULL
|
||||
};
|
||||
|
||||
struct rx_tx h264_rtp = {
|
||||
H264_STD,
|
||||
"H264 standard",
|
||||
h264_rtp_send,
|
||||
h264_rtp_done,
|
||||
NULL //TODO: h264_rtp_receiver_thread
|
||||
};
|
||||
|
||||
video_rxtx::video_rxtx(struct module *parent, struct video_export *video_exporter,
|
||||
const char *requested_compression): m_paused(false), m_compression(NULL),
|
||||
m_video_exporter(video_exporter) {
|
||||
|
||||
module_init_default(&m_sender_mod);
|
||||
m_sender_mod.cls = MODULE_CLASS_SENDER;
|
||||
module_register(&m_sender_mod, parent);
|
||||
|
||||
module_init_default(&m_receiver_mod);
|
||||
m_receiver_mod.cls = MODULE_CLASS_RECEIVER;
|
||||
module_register(&m_receiver_mod, parent);
|
||||
|
||||
int ret = compress_init(&m_sender_mod, requested_compression, &m_compression);
|
||||
if(ret != 0) {
|
||||
if(ret < 0) {
|
||||
throw string("Error initializing compression.");
|
||||
}
|
||||
if(ret > 0) {
|
||||
throw string("Error initializing compression.");
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_init(&m_lock, NULL);
|
||||
|
||||
if (pthread_create
|
||||
(&m_thread_id, NULL, video_rxtx::sender_thread,
|
||||
(void *) this) != 0) {
|
||||
throw string("Unable to create sender thread!\n");
|
||||
}
|
||||
}
|
||||
|
||||
video_rxtx::~video_rxtx() {
|
||||
send(NULL); // pass poisoned pill
|
||||
pthread_join(m_thread_id, NULL);
|
||||
|
||||
module_done(CAST_MODULE(m_compression));
|
||||
|
||||
module_done(&m_sender_mod);
|
||||
}
|
||||
|
||||
const char *video_rxtx::get_name(enum rxtx_protocol proto) {
|
||||
switch (proto) {
|
||||
case ULTRAGRID_RTP:
|
||||
return "UltraGrid RTP";
|
||||
}
|
||||
}
|
||||
|
||||
static void sage_rxtx_send(void *state, struct video_frame *tx_frame)
|
||||
{
|
||||
struct sage_rxtx_state *data = (struct sage_rxtx_state *) state;
|
||||
|
||||
if(!video_desc_eq(data->saved_vid_desc,
|
||||
video_desc_from_frame(tx_frame))) {
|
||||
display_reconfigure(data->sage_tx_device,
|
||||
video_desc_from_frame(tx_frame));
|
||||
data->saved_vid_desc = video_desc_from_frame(tx_frame);
|
||||
}
|
||||
struct video_frame *frame =
|
||||
display_get_frame(data->sage_tx_device);
|
||||
memcpy(frame->tiles[0].data, tx_frame->tiles[0].data,
|
||||
tx_frame->tiles[0].data_len);
|
||||
display_put_frame(data->sage_tx_device, frame, PUTF_NONBLOCK);
|
||||
|
||||
VIDEO_FRAME_DISPOSE(tx_frame);
|
||||
}
|
||||
|
||||
static void sage_rxtx_done(void *state)
|
||||
{
|
||||
struct sage_rxtx_state *data = (struct sage_rxtx_state *) state;
|
||||
|
||||
// poisoned pill to exit thread
|
||||
display_put_frame(data->sage_tx_device, NULL, PUTF_NONBLOCK);
|
||||
pthread_join(data->thread_id, NULL);
|
||||
|
||||
display_done(data->sage_tx_device);
|
||||
}
|
||||
|
||||
static void h264_rtp_send(void *state, struct video_frame *tx_frame)
|
||||
{
|
||||
struct h264_rtp_state *data = (struct h264_rtp_state *) state;
|
||||
|
||||
if(data->connections_count == 1) { /* normal/default case - only one connection */
|
||||
tx_send_h264(data->tx, tx_frame, data->network_devices[0]);
|
||||
} else {
|
||||
//TODO to be tested, the idea is to reply per destiny
|
||||
for (int i = 0; i < data->connections_count; ++i) {
|
||||
tx_send_h264(data->tx, tx_frame,
|
||||
data->network_devices[i]);
|
||||
}
|
||||
}
|
||||
|
||||
VIDEO_FRAME_DISPOSE(tx_frame);
|
||||
}
|
||||
|
||||
static void h264_rtp_done(void *state)
|
||||
{
|
||||
struct h264_rtp_state *data = (struct h264_rtp_state *) state;
|
||||
|
||||
if (data->tx) {
|
||||
module_done(CAST_MODULE(data->tx));
|
||||
}
|
||||
}
|
||||
|
||||
void video_rxtx::send(struct video_frame *frame) {
|
||||
compress_frame(m_compression, frame);
|
||||
}
|
||||
|
||||
void *video_rxtx::sender_thread(void *args) {
|
||||
return static_cast<video_rxtx *>(args)->sender_loop();
|
||||
}
|
||||
|
||||
void *video_rxtx::sender_loop() {
|
||||
struct video_desc saved_vid_desc;
|
||||
|
||||
memset(&saved_vid_desc, 0, sizeof(saved_vid_desc));
|
||||
|
||||
struct module *control_mod = get_module(get_root_module(&m_sender_mod), "control");
|
||||
struct stats *stat_data_sent = stats_new_statistics((struct control_state *)
|
||||
control_mod, "data");
|
||||
|
||||
while(1) {
|
||||
// process external messages
|
||||
struct message *msg_external;
|
||||
while((msg_external = check_message(&m_sender_mod))) {
|
||||
process_message((struct msg_sender *) msg_external);
|
||||
free_message(msg_external);
|
||||
}
|
||||
|
||||
struct video_frame *tx_frame = NULL;
|
||||
|
||||
tx_frame = compress_pop(m_compression);
|
||||
if (!tx_frame)
|
||||
goto exit;
|
||||
|
||||
video_export(m_video_exporter, tx_frame);
|
||||
|
||||
if (!m_paused) {
|
||||
send_frame(tx_frame);
|
||||
}
|
||||
|
||||
VIDEO_FRAME_DISPOSE(tx_frame);
|
||||
|
||||
if (dynamic_cast<rtp_video_rxtx *>(this)) {
|
||||
rtp_video_rxtx *rtp_rxtx = dynamic_cast<rtp_video_rxtx *>(this);
|
||||
stats_update_int(stat_data_sent,
|
||||
rtp_get_bytes_sent(rtp_rxtx->m_network_devices[0]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
exit:
|
||||
stats_destroy(stat_data_sent);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
114
src/video_rxtx.h
Normal file
114
src/video_rxtx.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* @file video_rxtx.h
|
||||
* @author Martin Pulec <pulec@cesnet.cz>
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2013-2014 CESNET z.s.p.o.
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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. Neither the name of CESNET 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 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.
|
||||
*/
|
||||
|
||||
#ifndef VIDEO_RXTX_H_
|
||||
#define VIDEO_RXTX_H_
|
||||
|
||||
#include "module.h"
|
||||
|
||||
struct display;
|
||||
struct module;
|
||||
struct video_compress;
|
||||
struct video_export;
|
||||
struct video_frame;
|
||||
|
||||
enum rxtx_protocol {
|
||||
ULTRAGRID_RTP,
|
||||
IHDTV,
|
||||
SAGE,
|
||||
H264_STD
|
||||
};
|
||||
|
||||
struct rx_tx {
|
||||
enum rxtx_protocol protocol;
|
||||
const char *name;
|
||||
void (*send)(void *, struct video_frame *);
|
||||
void (*done)(void *);
|
||||
void *(*receiver_thread)(void *);
|
||||
};
|
||||
|
||||
extern struct rx_tx ultragrid_rtp;
|
||||
extern struct rx_tx sage_rxtx;
|
||||
extern struct rx_tx h264_rtp;
|
||||
|
||||
struct sage_rxtx_state {
|
||||
struct video_desc saved_vid_desc;
|
||||
struct display *sage_tx_device;
|
||||
pthread_t thread_id;
|
||||
};
|
||||
|
||||
struct h264_rtp_state {
|
||||
int connections_count;
|
||||
struct rtp **network_devices;
|
||||
struct tx *tx;
|
||||
};
|
||||
|
||||
class video_rxtx {
|
||||
public:
|
||||
video_rxtx(struct module *parent, struct video_export *video_exporter,
|
||||
const char *requested_compression);
|
||||
virtual ~video_rxtx();
|
||||
void send(struct video_frame *);
|
||||
static const char *get_name(enum rxtx_protocol);
|
||||
static void *receiver_thread(void *arg) {
|
||||
video_rxtx *rxtx = static_cast<video_rxtx *>(arg);
|
||||
return rxtx->get_receiver_thread()(arg);
|
||||
}
|
||||
bool supports_receiving() {
|
||||
return get_receiver_thread() != NULL;
|
||||
}
|
||||
protected:
|
||||
bool m_paused;
|
||||
struct module m_sender_mod;
|
||||
struct module m_receiver_mod;
|
||||
private:
|
||||
virtual void send_frame(struct video_frame *) = 0;
|
||||
virtual void *(*get_receiver_thread())(void *arg) = 0;
|
||||
static void *sender_thread(void *args);
|
||||
void *sender_loop();
|
||||
virtual void process_message(struct msg_sender *) {
|
||||
}
|
||||
|
||||
struct compress_state *m_compression;
|
||||
pthread_mutex_t m_lock;
|
||||
struct video_export *m_video_exporter;
|
||||
|
||||
pthread_t m_thread_id;
|
||||
};
|
||||
|
||||
#endif // VIDEO_RXTX_H_
|
||||
|
||||
174
src/video_rxtx/rtp.cpp
Normal file
174
src/video_rxtx/rtp.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* @file video_rxtx/rtp.cpp
|
||||
* @author Martin Pulec <pulec@cesnet.cz>
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2013-2014 CESNET z.s.p.o.
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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. Neither the name of CESNET 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 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.
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#include "config_unix.h"
|
||||
#include "config_win32.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#include "video_rxtx/rtp.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "host.h"
|
||||
#include "ihdtv.h"
|
||||
#include "messaging.h"
|
||||
#include "module.h"
|
||||
#include "pdb.h"
|
||||
#include "rtp/rtp.h"
|
||||
#include "rtp/video_decoders.h"
|
||||
#include "rtp/pbuf.h"
|
||||
#include "tfrc.h"
|
||||
#include "stats.h"
|
||||
#include "transmit.h"
|
||||
#include "tv.h"
|
||||
#include "utils/vf_split.h"
|
||||
#include "video.h"
|
||||
#include "video_compress.h"
|
||||
#include "video_decompress.h"
|
||||
#include "video_display.h"
|
||||
#include "video_export.h"
|
||||
#include "video_rxtx.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
void rtp_video_rxtx::process_message(struct msg_sender *msg)
|
||||
{
|
||||
int ret;
|
||||
switch(msg->type) {
|
||||
case SENDER_MSG_CHANGE_RECEIVER:
|
||||
assert(m_connections_count == 1);
|
||||
ret = rtp_change_dest(m_network_devices[0],
|
||||
msg->receiver);
|
||||
|
||||
if(ret == FALSE) {
|
||||
fprintf(stderr, "Changing receiver to: %s failed!\n",
|
||||
msg->receiver);
|
||||
}
|
||||
break;
|
||||
case SENDER_MSG_CHANGE_PORT:
|
||||
change_tx_port(msg->port);
|
||||
break;
|
||||
case SENDER_MSG_PAUSE:
|
||||
m_paused = true;
|
||||
break;
|
||||
case SENDER_MSG_PLAY:
|
||||
m_paused = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rtp_video_rxtx::rtp_video_rxtx(struct module *parent,
|
||||
struct video_export *video_exporter,
|
||||
const char *requested_compression, const char *requested_encryption,
|
||||
const char *receiver, int rx_port, int tx_port,
|
||||
bool use_ipv6, const char *mcast_if, const char *requested_video_fec,
|
||||
int requested_mtu, long packet_rate) :
|
||||
video_rxtx(parent, video_exporter, requested_compression)
|
||||
{
|
||||
if(requested_mtu > RTP_MAX_PACKET_LEN) {
|
||||
ostringstream oss;
|
||||
oss << "Requested MTU exceeds maximal value allowed by RTP library (" <<
|
||||
RTP_MAX_PACKET_LEN << ").";
|
||||
throw oss.str();
|
||||
}
|
||||
|
||||
m_participants = pdb_init();
|
||||
m_requested_receiver = receiver;
|
||||
m_recv_port_number = rx_port;
|
||||
m_send_port_number = tx_port;
|
||||
m_ipv6 = use_ipv6;
|
||||
m_requested_mcast_if = mcast_if;
|
||||
|
||||
if ((m_network_devices = initialize_network(receiver, rx_port, tx_port,
|
||||
m_participants, use_ipv6, mcast_if))
|
||||
== NULL) {
|
||||
throw string("Unable to open network");
|
||||
} else {
|
||||
struct rtp **item;
|
||||
m_connections_count = 0;
|
||||
/* only count how many connections has initialize_network opened */
|
||||
for(item = m_network_devices; *item != NULL; ++item)
|
||||
++m_connections_count;
|
||||
}
|
||||
|
||||
if ((m_tx = tx_init(&m_sender_mod,
|
||||
requested_mtu, TX_MEDIA_VIDEO,
|
||||
requested_video_fec,
|
||||
requested_encryption, packet_rate)) == NULL) {
|
||||
throw string("Unable to initialize transmitter");
|
||||
}
|
||||
}
|
||||
|
||||
rtp_video_rxtx::~rtp_video_rxtx()
|
||||
{
|
||||
if (m_tx) {
|
||||
module_done(CAST_MODULE(m_tx));
|
||||
}
|
||||
|
||||
if (m_participants != NULL) {
|
||||
pdb_iter_t it;
|
||||
struct pdb_e *cp = pdb_iter_init(m_participants, &it);
|
||||
while (cp != NULL) {
|
||||
struct pdb_e *item = NULL;
|
||||
pdb_remove(m_participants, cp->ssrc, &item);
|
||||
cp = pdb_iter_next(&it);
|
||||
free(item);
|
||||
}
|
||||
pdb_iter_done(&it);
|
||||
pdb_destroy(&m_participants);
|
||||
}
|
||||
}
|
||||
|
||||
void rtp_video_rxtx::change_tx_port(int tx_port)
|
||||
{
|
||||
destroy_rtp_devices(m_network_devices);
|
||||
m_send_port_number = tx_port;
|
||||
m_network_devices = initialize_network(m_requested_receiver, m_recv_port_number,
|
||||
m_send_port_number, m_participants, m_ipv6,
|
||||
m_requested_mcast_if);
|
||||
if (!m_network_devices) {
|
||||
throw string("Changing RX port failed!\n");
|
||||
}
|
||||
}
|
||||
|
||||
68
src/video_rxtx/rtp.h
Normal file
68
src/video_rxtx/rtp.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @file video_rxtx/rtp.h
|
||||
* @author Martin Pulec <pulec@cesnet.cz>
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2013-2014 CESNET z.s.p.o.
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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. Neither the name of CESNET 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 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.
|
||||
*/
|
||||
|
||||
#ifndef VIDEO_RXTX_RTP_H_
|
||||
#define VIDEO_RXTX_RTP_H_
|
||||
|
||||
#include "video_rxtx.h"
|
||||
|
||||
class rtp_video_rxtx : public video_rxtx {
|
||||
friend class video_rxtx;
|
||||
public:
|
||||
rtp_video_rxtx(struct module *parent, struct video_export *video_exporter,
|
||||
const char *requested_compression, const char *requested_encryption,
|
||||
const char *receiver, int rx_port, int tx_port,
|
||||
bool use_ipv6, const char *mcast_if, const char *requested_video_fec, int mtu,
|
||||
long packet_rate);
|
||||
virtual ~rtp_video_rxtx();
|
||||
protected:
|
||||
int m_connections_count;
|
||||
struct rtp **m_network_devices; // ULTRAGRID_RTP
|
||||
struct tx *m_tx;
|
||||
struct pdb *m_participants;
|
||||
const char *m_requested_receiver;
|
||||
int m_recv_port_number;
|
||||
int m_send_port_number;
|
||||
bool m_ipv6;
|
||||
const char *m_requested_mcast_if;
|
||||
private:
|
||||
void process_message(struct msg_sender *);
|
||||
void change_tx_port(int tx_port);
|
||||
};
|
||||
|
||||
#endif // VIDEO_RXTX_RTP_H_
|
||||
|
||||
359
src/video_rxtx/ultragrid_rtp.cpp
Normal file
359
src/video_rxtx/ultragrid_rtp.cpp
Normal file
@@ -0,0 +1,359 @@
|
||||
/**
|
||||
* @file video_rxtx.cpp
|
||||
* @author Martin Pulec <pulec@cesnet.cz>
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2013-2014 CESNET z.s.p.o.
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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. Neither the name of CESNET 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 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.
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#include "config_unix.h"
|
||||
#include "config_win32.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "host.h"
|
||||
#include "ihdtv.h"
|
||||
#include "messaging.h"
|
||||
#include "module.h"
|
||||
#include "pdb.h"
|
||||
#include "rtp/rtp.h"
|
||||
#include "rtp/video_decoders.h"
|
||||
#include "rtp/pbuf.h"
|
||||
#include "tfrc.h"
|
||||
#include "stats.h"
|
||||
#include "transmit.h"
|
||||
#include "tv.h"
|
||||
#include "utils/vf_split.h"
|
||||
#include "video.h"
|
||||
#include "video_compress.h"
|
||||
#include "video_decompress.h"
|
||||
#include "video_display.h"
|
||||
#include "video_export.h"
|
||||
#include "video_rxtx.h"
|
||||
#include "video_rxtx/ultragrid_rtp.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
void ultragrid_rtp_video_rxtx::send_frame(struct video_frame *tx_frame)
|
||||
{
|
||||
if (m_connections_count == 1) { /* normal case - only one connection */
|
||||
tx_send(m_tx, tx_frame,
|
||||
m_network_devices[0]);
|
||||
} else { /* split */
|
||||
struct video_frame *split_frames = vf_alloc(m_connections_count);
|
||||
|
||||
//assert(frame_count == 1);
|
||||
vf_split_horizontal(split_frames, tx_frame,
|
||||
m_connections_count);
|
||||
for (int i = 0; i < m_connections_count; ++i) {
|
||||
tx_send_tile(m_tx, split_frames, i,
|
||||
m_network_devices[i]);
|
||||
}
|
||||
|
||||
vf_free(split_frames);
|
||||
}
|
||||
}
|
||||
|
||||
ultragrid_rtp_video_rxtx::~ultragrid_rtp_video_rxtx()
|
||||
{
|
||||
}
|
||||
|
||||
void ultragrid_rtp_video_rxtx::receiver_process_messages()
|
||||
{
|
||||
struct msg_receiver *msg;
|
||||
while ((msg = (struct msg_receiver *) check_message(&m_receiver_mod))) {
|
||||
switch (msg->type) {
|
||||
case RECEIVER_MSG_CHANGE_RX_PORT:
|
||||
assert(rxtx_mode == MODE_RECEIVER); // receiver only
|
||||
destroy_rtp_devices(m_network_devices);
|
||||
m_recv_port_number = msg->new_rx_port;
|
||||
m_network_devices = initialize_network(m_requested_receiver, m_recv_port_number,
|
||||
m_send_port_number, m_participants, m_ipv6,
|
||||
m_requested_mcast_if);
|
||||
if (!m_network_devices) {
|
||||
throw runtime_error("Changing RX port failed!");
|
||||
}
|
||||
break;
|
||||
case RECEIVER_MSG_VIDEO_PROP_CHANGED:
|
||||
{
|
||||
pdb_iter_t it;
|
||||
/// @todo should be set only to relevant participant, not all
|
||||
struct pdb_e *cp = pdb_iter_init(m_participants, &it);
|
||||
while (cp) {
|
||||
pbuf_set_playout_delay(cp->playout_buffer,
|
||||
1.0 / msg->new_desc.fps,
|
||||
1.0 / msg->new_desc.fps *
|
||||
(is_codec_interframe(msg->new_desc.color_spec) ? 2.2 : 1.2)
|
||||
);
|
||||
|
||||
cp = pdb_iter_next(&it);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
free_message((struct message *) msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes display from decoders and effectively kills them. They cannot be used
|
||||
* until new display assigned.
|
||||
*/
|
||||
void ultragrid_rtp_video_rxtx::remove_display_from_decoders() {
|
||||
if (m_participants != NULL) {
|
||||
pdb_iter_t it;
|
||||
struct pdb_e *cp = pdb_iter_init(m_participants, &it);
|
||||
while (cp != NULL) {
|
||||
if(cp->decoder_state)
|
||||
video_decoder_remove_display(
|
||||
((struct vcodec_state*) cp->decoder_state)->decoder);
|
||||
cp = pdb_iter_next(&it);
|
||||
}
|
||||
pdb_iter_done(&it);
|
||||
}
|
||||
}
|
||||
|
||||
void ultragrid_rtp_video_rxtx::destroy_video_decoder(void *state) {
|
||||
struct vcodec_state *video_decoder_state = (struct vcodec_state *) state;
|
||||
|
||||
if(!video_decoder_state) {
|
||||
return;
|
||||
}
|
||||
|
||||
video_decoder_destroy(video_decoder_state->decoder);
|
||||
|
||||
free(video_decoder_state);
|
||||
}
|
||||
|
||||
struct vcodec_state *ultragrid_rtp_video_rxtx::new_video_decoder() {
|
||||
struct vcodec_state *state = (struct vcodec_state *) calloc(1, sizeof(struct vcodec_state));
|
||||
|
||||
if(state) {
|
||||
state->decoder = video_decoder_init(&m_receiver_mod, m_decoder_mode,
|
||||
m_postprocess, m_display_device,
|
||||
m_requested_encryption);
|
||||
|
||||
if(!state->decoder) {
|
||||
fprintf(stderr, "Error initializing decoder (incorrect '-M' or '-p' option?).\n");
|
||||
free(state);
|
||||
exit_uv(1);
|
||||
return NULL;
|
||||
} else {
|
||||
//decoder_register_display(state->decoder, uv->display_device);
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
void *ultragrid_rtp_video_rxtx::receiver_loop()
|
||||
{
|
||||
uint32_t ts;
|
||||
struct pdb_e *cp;
|
||||
struct timeval curr_time;
|
||||
int fr;
|
||||
int ret;
|
||||
unsigned int tiles_post = 0;
|
||||
struct timeval last_tile_received = {0, 0};
|
||||
int last_buf_size = INITIAL_VIDEO_RECV_BUFFER_SIZE;
|
||||
#ifdef SHARED_DECODER
|
||||
struct vcodec_state *shared_decoder = new_decoder(uv);
|
||||
if(shared_decoder == NULL) {
|
||||
fprintf(stderr, "Unable to create decoder!\n");
|
||||
exit_uv(1);
|
||||
return NULL;
|
||||
}
|
||||
#endif // SHARED_DECODER
|
||||
|
||||
initialize_video_decompress();
|
||||
|
||||
fr = 1;
|
||||
|
||||
struct module *control_mod = get_module(get_root_module(&m_sender_mod), "control");
|
||||
struct stats *stat_loss = stats_new_statistics(
|
||||
(struct control_state *) control_mod,
|
||||
"loss");
|
||||
struct stats *stat_received = stats_new_statistics(
|
||||
(struct control_state *) control_mod,
|
||||
"received");
|
||||
uint64_t total_received = 0ull;
|
||||
|
||||
while (!should_exit_receiver) {
|
||||
struct timeval timeout;
|
||||
/* Housekeeping and RTCP... */
|
||||
gettimeofday(&curr_time, NULL);
|
||||
ts = tv_diff(curr_time, m_start_time) * 90000;
|
||||
rtp_update(m_network_devices[0], curr_time);
|
||||
rtp_send_ctrl(m_network_devices[0], ts, 0, curr_time);
|
||||
|
||||
/* Receive packets from the network... The timeout is adjusted */
|
||||
/* to match the video capture rate, so the transmitter works. */
|
||||
if (fr) {
|
||||
gettimeofday(&curr_time, NULL);
|
||||
receiver_process_messages();
|
||||
fr = 0;
|
||||
}
|
||||
|
||||
timeout.tv_sec = 0;
|
||||
//timeout.tv_usec = 999999 / 59.94;
|
||||
timeout.tv_usec = 10000;
|
||||
ret = rtp_recv_poll_r(m_network_devices, &timeout, ts);
|
||||
|
||||
// timeout
|
||||
if (ret == FALSE) {
|
||||
// processing is needed here in case we are not receiving any data
|
||||
receiver_process_messages();
|
||||
//printf("Failed to receive data\n");
|
||||
}
|
||||
total_received += ret;
|
||||
stats_update_int(stat_received, total_received);
|
||||
|
||||
/* Decode and render for each participant in the conference... */
|
||||
pdb_iter_t it;
|
||||
cp = pdb_iter_init(m_participants, &it);
|
||||
while (cp != NULL) {
|
||||
if (tfrc_feedback_is_due(cp->tfrc_state, curr_time)) {
|
||||
debug_msg("tfrc rate %f\n",
|
||||
tfrc_feedback_txrate(cp->tfrc_state,
|
||||
curr_time));
|
||||
}
|
||||
|
||||
if(cp->decoder_state == NULL &&
|
||||
!pbuf_is_empty(cp->playout_buffer)) { // the second check is needed because we want to assign display to participant that really sends data
|
||||
#ifdef SHARED_DECODER
|
||||
cp->decoder_state = shared_decoder;
|
||||
#else
|
||||
// we are assigning our display so we make sure it is removed from other dispaly
|
||||
remove_display_from_decoders();
|
||||
cp->decoder_state = new_video_decoder();
|
||||
cp->decoder_state_deleter = destroy_video_decoder;
|
||||
#endif // SHARED_DECODER
|
||||
if (cp->decoder_state == NULL) {
|
||||
fprintf(stderr, "Fatal: unable to create decoder state for "
|
||||
"participant %u.\n", cp->ssrc);
|
||||
exit_uv(1);
|
||||
break;
|
||||
}
|
||||
((struct vcodec_state*) cp->decoder_state)->display = m_display_device;
|
||||
}
|
||||
|
||||
struct vcodec_state *vdecoder_state = (struct vcodec_state *) cp->decoder_state;
|
||||
|
||||
/* Decode and render video... */
|
||||
if (pbuf_decode
|
||||
(cp->playout_buffer, curr_time, decode_video_frame, vdecoder_state)) {
|
||||
tiles_post++;
|
||||
/* we have data from all connections we need */
|
||||
if(tiles_post == m_connections_count)
|
||||
{
|
||||
tiles_post = 0;
|
||||
gettimeofday(&curr_time, NULL);
|
||||
fr = 1;
|
||||
#if 0
|
||||
display_put_frame(uv->display_device,
|
||||
cp->video_decoder_state->frame_buffer);
|
||||
cp->video_decoder_state->frame_buffer =
|
||||
display_get_frame(uv->display_device);
|
||||
#endif
|
||||
}
|
||||
last_tile_received = curr_time;
|
||||
uint32_t sender_ssrc = cp->ssrc;
|
||||
stats_update_int(stat_loss,
|
||||
rtp_compute_fract_lost(m_network_devices[0],
|
||||
sender_ssrc));
|
||||
}
|
||||
|
||||
/* dual-link TIMEOUT - we won't wait for next tiles */
|
||||
if(tiles_post > 1 && tv_diff(curr_time, last_tile_received) >
|
||||
999999 / 59.94 / m_connections_count) {
|
||||
tiles_post = 0;
|
||||
gettimeofday(&curr_time, NULL);
|
||||
fr = 1;
|
||||
#if 0
|
||||
display_put_frame(uv->display_device,
|
||||
cp->video_decoder_state->frame_buffer);
|
||||
cp->video_decoder_state->frame_buffer =
|
||||
display_get_frame(uv->display_device);
|
||||
#endif
|
||||
last_tile_received = curr_time;
|
||||
}
|
||||
|
||||
if(vdecoder_state && vdecoder_state->decoded % 100 == 99) {
|
||||
int new_size = vdecoder_state->max_frame_size * 110ull / 100;
|
||||
if(new_size > last_buf_size) {
|
||||
struct rtp **device = m_network_devices;
|
||||
while(*device) {
|
||||
int ret = rtp_set_recv_buf(*device, new_size);
|
||||
if(!ret) {
|
||||
display_buf_increase_warning(new_size);
|
||||
}
|
||||
debug_msg("Recv buffer adjusted to %d\n", new_size);
|
||||
device++;
|
||||
}
|
||||
}
|
||||
last_buf_size = new_size;
|
||||
}
|
||||
|
||||
pbuf_remove(cp->playout_buffer, curr_time);
|
||||
cp = pdb_iter_next(&it);
|
||||
}
|
||||
pdb_iter_done(&it);
|
||||
}
|
||||
|
||||
module_done(&m_receiver_mod);
|
||||
|
||||
#ifdef SHARED_DECODER
|
||||
destroy_decoder(shared_decoder);
|
||||
#else
|
||||
/* Because decoders work asynchronously we need to make sure
|
||||
* that display won't be called */
|
||||
remove_display_from_decoders();
|
||||
#endif // SHARED_DECODER
|
||||
|
||||
// pass posioned pill to display
|
||||
display_put_frame(m_display_device, NULL, PUTF_BLOCKING);
|
||||
|
||||
stats_destroy(stat_loss);
|
||||
stats_destroy(stat_received);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
89
src/video_rxtx/ultragrid_rtp.h
Normal file
89
src/video_rxtx/ultragrid_rtp.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* @file video_rxtx/ultragrid_rtp.h
|
||||
* @author Martin Pulec <pulec@cesnet.cz>
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2013-2014 CESNET z.s.p.o.
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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. Neither the name of CESNET 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 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.
|
||||
*/
|
||||
|
||||
#ifndef VIDEO_RXTX_ULTRAGRID_RTP_H_
|
||||
#define VIDEO_RXTX_ULTRAGRID_RTP_H_
|
||||
|
||||
#include "video_rxtx.h"
|
||||
#include "video_rxtx/rtp.h"
|
||||
|
||||
class ultragrid_rtp_video_rxtx : public rtp_video_rxtx {
|
||||
public:
|
||||
ultragrid_rtp_video_rxtx(struct module *parent, struct video_export *video_exporter,
|
||||
const char *requested_compression, const char *requested_encryption,
|
||||
const char *receiver, int rx_port, int tx_port,
|
||||
bool use_ipv6, const char *mcast_if, const char *requested_video_fec, int mtu,
|
||||
long packet_rate, enum video_mode decoder_mode, const char *postprocess,
|
||||
struct display *display_device) :
|
||||
rtp_video_rxtx(parent, video_exporter, requested_compression, requested_encryption,
|
||||
receiver, rx_port, tx_port,
|
||||
use_ipv6, mcast_if, requested_video_fec, mtu, packet_rate)
|
||||
{
|
||||
gettimeofday(&m_start_time, NULL);
|
||||
m_decoder_mode = decoder_mode;
|
||||
m_postprocess = postprocess;
|
||||
m_display_device = display_device;
|
||||
m_requested_encryption = requested_encryption;
|
||||
}
|
||||
virtual ~ultragrid_rtp_video_rxtx();
|
||||
static void *receiver_thread(void *arg) {
|
||||
ultragrid_rtp_video_rxtx *s = static_cast<ultragrid_rtp_video_rxtx *>(arg);
|
||||
return s->receiver_loop();
|
||||
}
|
||||
void *receiver_loop();
|
||||
protected:
|
||||
virtual void send_frame(struct video_frame *);
|
||||
private:
|
||||
virtual void *(*get_receiver_thread())(void *arg) {
|
||||
return receiver_thread;
|
||||
}
|
||||
|
||||
void receiver_process_messages();
|
||||
void remove_display_from_decoders();
|
||||
struct vcodec_state *new_video_decoder();
|
||||
static void destroy_video_decoder(void *state);
|
||||
|
||||
struct timeval m_start_time;
|
||||
|
||||
enum video_mode m_decoder_mode;
|
||||
const char *m_postprocess;
|
||||
struct display *m_display_device;
|
||||
const char *m_requested_encryption;
|
||||
};
|
||||
|
||||
#endif // VIDEO_RXTX_ULTRAGRID_RTP_H_
|
||||
|
||||
Reference in New Issue
Block a user