Added possibility to send throught multiple links

+ basis for 4K transmit
This commit is contained in:
Martin Pulec
2011-04-28 16:41:50 +02:00
parent 23a0237780
commit 3c912d7f55
13 changed files with 651 additions and 67 deletions

View File

@@ -35,6 +35,7 @@ OBJS = src/bitstream.o \
src/crypto/md5.o \
src/crypto/random.o \
src/video_codec.o \
src/tile.o \
src/video_capture.o \
src/video_capture/null.o \
@DVS_OBJ@ \

View File

@@ -84,8 +84,13 @@
#define EXIT_FAIL_NETWORK 5
#define EXIT_FAIL_TRANSMIT 6
#define PORT_BASE 5004
#define PORT_AUDIO 5006
struct state_uv {
struct rtp *network_device;
struct rtp **network_devices;
unsigned int connections_count;
int split_frames;
struct rtp *audio_network_device;
struct vidcap *capture_device;
struct timeval start_time, curr_time;
@@ -131,7 +136,7 @@ static void usage(void)
{
/* TODO -c -p -b are deprecated options */
printf
("Usage: uv [-d <display_device>] [-t <capture_device>] [-g <cfg>] [-m <mtu>] [-f <framerate>] [-c] [-p] [-i] [-b <8|10>] address\n\n");
("Usage: uv [-d <display_device>] [-t <capture_device>] [-g <cfg>] [-m <mtu>] [-f <framerate>] [-c] [-p] [-i] [-b <8|10>] [-S] address(es)\n\n");
printf
("\t-d <display_device>\tselect display device, use -d help to get\n");
printf("\t \tlist of devices availabe\n");
@@ -143,6 +148,10 @@ static void usage(void)
("\t \tuse -g help with a device to get info about\n");
printf("\t \tsupported capture/display modes\n");
printf("\t-i \tiHDTV compatibility mode\n");
printf("\t-S \tsplit frame (multilink mode)\n");
printf("\taddresses \tcomma-separated list of destination interfaces'\n");
printf("\t \taddresses (splitted frame or tiled video)\n");
printf("\t \tfirst case implies '-S' argument\n");
}
void list_video_display_devices()
@@ -226,20 +235,71 @@ static struct vidcap *initialize_video_capture(const char *requested_capture,
return vidcap_init(id, fmt);
}
static struct rtp *initialize_network(char *addr, struct pdb *participants)
static struct rtp **initialize_network(char *addrs, struct pdb *participants)
{
struct rtp *r;
struct rtp **devices = NULL;
double rtcp_bw = 5 * 1024 * 1024; /* FIXME */
int ttl = 255;
char *saveptr;
char *addr;
char *tmp;
int required_connections, index;
int port = PORT_BASE;
r = rtp_init(addr, 5004, 5004, 255, rtcp_bw, FALSE, rtp_recv_callback,
(void *)participants);
if (r != NULL) {
pdb_add(participants, rtp_my_ssrc(r));
rtp_set_option(r, RTP_OPT_WEAK_VALIDATION, TRUE);
rtp_set_sdes(r, rtp_my_ssrc(r), RTCP_SDES_TOOL,
ULTRAGRID_VERSION, strlen(ULTRAGRID_VERSION));
}
return r;
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(addrs, ",", &saveptr);
index < required_connections;
++index, addr = strtok_r(NULL, ",", &saveptr), port += 2)
{
if (port == PORT_AUDIO)
port += 2;
devices[index] = rtp_init(addr, port, port, ttl, rtcp_bw,
FALSE, rtp_recv_callback,
(void *)participants);
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,
ULTRAGRID_VERSION, strlen(ULTRAGRID_VERSION));
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;
}
static void destroy_devices(struct rtp ** network_devices)
{
struct rtp ** current = network_devices;
while(current != NULL) {
rtp_done(*current++);
}
free(network_devices);
}
static struct video_tx *initialize_transmit(unsigned requested_mtu)
@@ -426,8 +486,8 @@ static void *receiver_thread(void *arg)
/* Housekeeping and RTCP... */
gettimeofday(&uv->curr_time, NULL);
uv->ts = tv_diff(uv->curr_time, uv->start_time) * 90000;
rtp_update(uv->network_device, uv->curr_time);
rtp_send_ctrl(uv->network_device, uv->ts, 0, uv->curr_time);
rtp_update(uv->network_devices[0], uv->curr_time);
rtp_send_ctrl(uv->network_devices[0], uv->ts, 0, uv->curr_time);
/* Receive packets from the network... The timeout is adjusted */
/* to match the video capture rate, so the transmitter works. */
@@ -439,7 +499,7 @@ static void *receiver_thread(void *arg)
timeout.tv_sec = 0;
timeout.tv_usec = 999999 / 59.94;
ret = rtp_recv(uv->network_device, &timeout, uv->ts);
ret = rtp_recv_poll(uv->network_devices, &timeout, uv->ts);
/*
if (ret == FALSE) {
@@ -483,6 +543,19 @@ static void *sender_thread(void *arg)
struct video_frame *tx_frame;
struct video_frame *splitted_frames = NULL;
struct tile_info t_info;
int net_dev = 0;
if(uv->split_frames) {
/* it is simply stripping frame */
t_info.x_count = 1u;
t_info.y_count = uv->connections_count;
splitted_frames = (struct video_frame *)
malloc(uv->connections_count *
sizeof(struct video_frame));
}
while (!should_exit) {
/* Capture and transmit video... */
tx_frame = vidcap_grab(uv->capture_device);
@@ -493,9 +566,22 @@ static void *sender_thread(void *arg)
compress_data(uv->compression, tx_frame);
#endif /* HAVE_FASTDXT */
}
tx_send(uv->tx, tx_frame, uv->network_device);
if(!uv->split_frames) {
tx_send(uv->tx, tx_frame,
uv->network_devices[net_dev]);
net_dev = (net_dev + 1) % uv->connections_count;
} else { /* split */
int i;
vf_split_horizontal(splitted_frames, tx_frame,
t_info.y_count);
for (i = 0; i < uv->connections_count; ++i) {
tx_send(uv->tx, &splitted_frames[i],
uv->network_devices[i]);
}
}
}
}
free(splitted_frames);
return 0;
}
@@ -524,6 +610,8 @@ int main(int argc, char *argv[])
{"ihdtv", no_argument, 0, 'i'},
{"receive", required_argument, 0, 'r'},
{"send", required_argument, 0, 's'},
{"split", no_argument, 0, 'S'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
int option_index = 0;
@@ -544,14 +632,15 @@ int main(int argc, char *argv[])
uv->audio_playback_device = -2;
uv->audio_participants = NULL;
uv->participants = NULL;
uv->split_frames = 0;
#ifdef HAVE_AUDIO
while ((ch =
getopt_long(argc, argv, "d:g:t:m:f:b:r:s:vcpi", getopt_options,
getopt_long(argc, argv, "d:g:t:m:f:b:r:s:vcpihS", getopt_options,
&option_index)) != -1) {
#else
while ((ch =
getopt_long(argc, argv, "d:g:t:m:f:b:vcpi", getopt_options,
getopt_long(argc, argv, "d:g:t:m:f:b:vcpihS", getopt_options,
&option_index)) != -1) {
#endif /* HAVE_AUDIO */
switch (ch) {
@@ -616,6 +705,12 @@ int main(int argc, char *argv[])
uv->audio_capture_device = atoi(optarg);
break;
#endif /* HAVE_AUDIO */
case 'S':
uv->split_frames = 1;
break;
case 'h':
usage();
return 0;
case '?':
break;
default:
@@ -703,14 +798,20 @@ int main(int argc, char *argv[])
pthread_t audio_thread_id;
if ((uv->audio_playback_device != -2)
|| (uv->audio_capture_device != -2)) {
uv->audio_participants = pdb_init();
char *tmp;
char *addr;
tmp = strdup(argv[0]);
uv->audio_participants = pdb_init();
addr = strtok_r(tmp, NULL, NULL);
if ((uv->audio_network_device =
initialize_audio_network(argv[0],
initialize_audio_network(addr,
uv->audio_participants)) ==
NULL) {
printf("Unable to open audio network\n");
free(tmp);
return EXIT_FAIL_NETWORK;
}
free(tmp);
if (pthread_create
(&audio_thread_id, NULL, audio_thread, (void *)uv) != 0) {
@@ -794,10 +895,15 @@ int main(int argc, char *argv[])
sleep(1);
} else {
if ((uv->network_device =
if ((uv->network_devices =
initialize_network(argv[0], uv->participants)) == NULL) {
printf("Unable to open network\n");
return EXIT_FAIL_NETWORK;
} else {
struct rtp **item;
uv->connections_count = 0;
for(item = uv->network_devices; *item != NULL; ++item)
++uv->connections_count;
}
if (uv->requested_mtu == 0) // mtu wasn't specified on the command line
@@ -841,7 +947,7 @@ int main(int argc, char *argv[])
#endif /* HAVE_AUDIO */
tx_done(uv->tx);
rtp_done(uv->network_device);
destroy_devices(uv->network_devices);
vidcap_done(uv->capture_device);
display_done(uv->display_device);
if (uv->participants != NULL)

View File

@@ -58,6 +58,7 @@ void decode_frame(struct coded_data *cdata, struct video_frame *frame)
uint32_t height;
uint32_t offset;
uint32_t aux;
struct tile_info tile_info;
int len;
codec_t color_spec;
rtp_packet *pckt;
@@ -66,6 +67,7 @@ void decode_frame(struct coded_data *cdata, struct video_frame *frame)
uint32_t data_pos;
int prints=0;
double fps;
struct video_frame *tile;
if(!frame)
return;
@@ -80,20 +82,27 @@ void decode_frame(struct coded_data *cdata, struct video_frame *frame)
data_pos = ntohl(hdr->offset);
fps = ntohl(hdr->fps)/65536.0;
aux = ntohl(hdr->aux);
tile_info = ntoh_uint2tileinfo(hdr->tileinfo);
/* Critical section
* each thread *MUST* wait here if this condition is true
*/
if (!(frame->width == width &&
frame->height == height &&
if (!(frame->width == (aux & AUX_TILED ? width * tile_info.x_count : width) &&
frame->height == (aux & AUX_TILED ? height * tile_info.y_count : height) &&
frame->color_spec == color_spec &&
frame->aux == aux &&
frame->fps == fps)) {
frame->fps == fps &&
(!(aux & AUX_TILED) || /* |- (tiled -> eq) <-> (!tiled OR eq) */
tileinfo_eq_count(frame->tile_info, tile_info)))) {
frame->reconfigure(frame->state, width, height,
color_spec, fps, aux);
color_spec, fps, aux, tile_info);
frame->src_linesize =
vc_getsrc_linesize(width, color_spec);
vc_getsrc_linesize(frame->width, color_spec);
}
if (aux & AUX_TILED)
tile = frame->get_tile_buffer(frame->state, tile_info);
else
tile = frame;
/* End of critical section */
/* MAGIC, don't touch it, you definitely break it
@@ -103,15 +112,16 @@ void decode_frame(struct coded_data *cdata, struct video_frame *frame)
/* compute Y pos in source frame and convert it to
* byte offset in the destination frame
*/
int y = (data_pos / frame->src_linesize) * frame->dst_pitch;
int y = (data_pos / tile->src_linesize) * frame->dst_pitch;
/* compute X pos in source frame */
int s_x = data_pos % frame->src_linesize;
int s_x = data_pos % tile->src_linesize;
/* convert X pos from source frame into the destination frame.
* it is byte offset from the beginning of a line.
*/
int d_x = ((int)((s_x) / frame->src_bpp)) * frame->dst_bpp;
int d_x = tile->dst_x_offset + ((int)((s_x) / tile->src_bpp)) *
frame->dst_bpp;
/* pointer to data payload in packet */
source = (unsigned char*)(pckt->data + sizeof(payload_hdr_t));
@@ -123,32 +133,32 @@ void decode_frame(struct coded_data *cdata, struct video_frame *frame)
/* len id payload length in source BPP
* decoder needs len in destination BPP, so convert it
*/
int l = ((int)(len / frame->src_bpp)) * frame->dst_bpp;
int l = ((int)(len / tile->src_bpp)) * frame->dst_bpp;
/* do not copy multiple lines, we need to
* copy (& clip, center) line by line
*/
if (l + d_x > (int)frame->dst_linesize) {
l = frame->dst_linesize - d_x;
if (l + d_x > (int)tile->dst_linesize) {
l = tile->dst_linesize - d_x;
}
/* compute byte offset in destination frame */
offset = y + d_x;
/* watch the SEGV */
if (l + offset <= frame->data_len) {
if (l + offset <= tile->data_len) {
/*decode frame:
* we have offset for destination
* we update source contiguously
* we pass {r,g,b}shifts */
frame->decoder((unsigned char*)frame->data + offset, source, l,
frame->decoder((unsigned char*)tile->data + offset, source, l,
frame->rshift, frame->gshift,
frame->bshift);
/* we decoded one line (or a part of one line) to the end of the line
* so decrease *source* len by 1 line (or that part of the line */
len -= frame->src_linesize - s_x;
len -= tile->src_linesize - s_x;
/* jump in source by the same amount */
source += frame->src_linesize - s_x;
source += tile->src_linesize - s_x;
} else {
/* this should not ever happen as we call reconfigure before each packet
* iff reconfigure is needed. But if it still happens, something is terribly wrong
@@ -162,7 +172,7 @@ void decode_frame(struct coded_data *cdata, struct video_frame *frame)
len = 0;
}
/* each new line continues from the beginning */
d_x = 0; /* next line from beginning */
d_x = tile->dst_x_offset; /* next line from beginning */
s_x = 0;
y += frame->dst_pitch; /* next line */
}

View File

@@ -2155,6 +2155,48 @@ int rtp_recv(struct rtp *session, struct timeval *timeout, uint32_t curr_rtp_ts)
return FALSE;
}
/**
* rtp_recv_poll:
* The meaning is as above with except that this function polls for first
* nonempty stream and returns data.
*
* @param sessions null-terminated list of rtp sessions.
* @param timeout timeout
* @param cur_rtp_ts list null-terminated of timestamps for each session
*/
int rtp_recv_poll(struct rtp **sessions, struct timeval *timeout, uint32_t curr_rtp_ts)
{
struct rtp **current;
udp_fd_zero();
for(current = sessions; *current != NULL; ++current) {
check_database(*current);
udp_fd_set((*current)->rtp_socket);
udp_fd_set((*current)->rtcp_socket);
}
if (udp_select(timeout) > 0) {
for(current = sessions; *current != NULL; ++current) {
if (udp_fd_isset((*current)->rtp_socket)) {
rtp_recv_data(*current, curr_rtp_ts);
}
if (udp_fd_isset((*current)->rtcp_socket)) {
uint8_t buffer[RTP_MAX_PACKET_LEN];
int buflen;
buflen =
udp_recv((*current)->rtcp_socket, (char *)buffer,
RTP_MAX_PACKET_LEN);
ntp64_time(&tmp_sec, &tmp_frac);
rtp_process_ctrl(*current, buffer, buflen);
}
check_database(*current);
}
return TRUE;
}
//check_database(session);
return FALSE;
}
/**
* rtp_add_csrc:
* @session: the session pointer (returned by rtp_init())

View File

@@ -222,6 +222,8 @@ int rtp_get_option(struct rtp *session, rtp_option optname, int *optval);
int rtp_recv(struct rtp *session,
struct timeval *timeout, uint32_t curr_rtp_ts);
int rtp_recv_poll(struct rtp **sessions,
struct timeval *timeout, uint32_t curr_rtp_ts);
int rtp_send_data(struct rtp *session,
uint32_t rtp_ts, char pt, int m,

View File

@@ -59,6 +59,7 @@ typedef struct {
uint8_t flags;
uint32_t fps; /* fixed point fps. take care! */
uint32_t aux; /* auxiliary data */
uint32_t tileinfo; /* info about tile position (if tiled) */
} payload_hdr_t;
/* FIXME: this is only needed because fdisplay() takes "struct display" as a parameter */

179
ultragrid/src/tile.c Normal file
View File

@@ -0,0 +1,179 @@
/*
* FILE: tile.c
* AUTHORS: 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>
*
* Copyright (c) 2005-2010 CESNET z.s.p.o.
*
* 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 CESNET z.s.p.o.
*
* 4. Neither the name of the 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.
*
*/
#include "config.h"
#include "config_unix.h"
#include "config_win32.h"
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "video_codec.h"
#include "tile.h"
#define MAGIC_H 0xFF
#define MAGIC_T 0xFE
void vf_split(struct video_frame *out, struct video_frame *src,
unsigned int x_count, unsigned int y_count, int preallocate)
{
unsigned int tile_idx, line_idx;
struct video_frame *cur_tiles;
unsigned int tile_line;
assert(src->width % x_count == 0u && src->height % y_count == 0u);
for(tile_idx = 0u; tile_idx < x_count * y_count; ++tile_idx) {
out[tile_idx].width = src->width / x_count;
out[tile_idx].height = src->height / y_count;
out[tile_idx].color_spec = src->color_spec;
out[tile_idx].aux = src->aux | AUX_TILED;
out[tile_idx].fps = src->fps;
out[tile_idx].tile_info.x_count = x_count;
out[tile_idx].tile_info.y_count = y_count;
out[tile_idx].tile_info.pos_x = tile_idx % x_count;
out[tile_idx].tile_info.pos_y = tile_idx / x_count;
out[tile_idx].src_linesize = vc_getsrc_linesize(out[tile_idx].width,
src->color_spec);
out[tile_idx].data_len = out[tile_idx].src_linesize * out[tile_idx].height;
}
cur_tiles = out;
for(line_idx = 0u; line_idx < src->height; ++line_idx, ++tile_line) {
unsigned int cur_tile_idx;
unsigned int byte = 0u;
if (line_idx % (src->height / y_count) == 0u) /* next tiles*/
{
tile_line = 0u;
if (line_idx != 0u)
cur_tiles += x_count;
if (preallocate) {
for (cur_tile_idx = 0u; cur_tile_idx < x_count;
++cur_tile_idx) {
cur_tiles[cur_tile_idx].data =
malloc(cur_tiles[cur_tile_idx].
data_len);
}
}
}
for(cur_tile_idx = 0u; cur_tile_idx < x_count; ++cur_tile_idx) {
memcpy((void *) &cur_tiles[cur_tile_idx].data[
tile_line *
cur_tiles[cur_tile_idx].src_linesize],
(void *) &src->data[line_idx *
src->src_linesize + byte],
cur_tiles[cur_tile_idx].width *
get_bpp(src->color_spec));
byte += cur_tiles[cur_tile_idx].width * get_bpp(src->color_spec);
}
}
}
void vf_split_horizontal(struct video_frame *out, struct video_frame *src,
unsigned int y_count)
{
unsigned int i;
for(i = 0u; i < y_count; ++i) {
out[i].width = src->width;
out[i].height = src->height / y_count;
out[i].color_spec = src->color_spec;
out[i].aux = src->aux | AUX_TILED;
out[i].fps = src->fps;
out[i].tile_info.x_count = 1u;
out[i].tile_info.y_count = y_count;
out[i].tile_info.pos_x = 0u;
out[i].tile_info.pos_y = i;
out[i].src_linesize = vc_getsrc_linesize(out[i].width,
src->color_spec);
out[i].data_len = out[i].src_linesize * out[i].height;
out[i].data = src->data + i * out[i].height * src->src_linesize;
}
}
uint32_t hton_tileinfo2uint(struct tile_info tile_info)
{
union {
struct tile_info t_info;
uint32_t res;
} trans;
trans.t_info = tile_info;
trans.t_info.h_reserved = MAGIC_H;
trans.t_info.t_reserved = MAGIC_T;
return htonl(trans.res);
}
struct tile_info ntoh_uint2tileinfo(uint32_t packed)
{
union {
struct tile_info t_info;
uint32_t src;
} trans;
trans.src = ntohl(packed);
if(trans.t_info.h_reserved = MAGIC_H)
return trans.t_info;
else { /* == MAGIC_T */
int tmp;
tmp = trans.t_info.x_count << 4u & trans.t_info.y_count;
trans.t_info.x_count = trans.t_info.pos_x;
trans.t_info.y_count = trans.t_info.pos_y;
trans.t_info.pos_x = tmp >> 4u;
trans.t_info.pos_y = tmp & 0xF;
return trans.t_info;
}
}
int tileinfo_eq_count(struct tile_info t1, struct tile_info t2)
{
return t1.x_count == t2.x_count && t1.y_count == t2.y_count;
}

100
ultragrid/src/tile.h Normal file
View File

@@ -0,0 +1,100 @@
/*
* FILE: tile.h
* AUTHORS: 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>
*
* Copyright (c) 2005-2010 CESNET z.s.p.o.
*
* 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 CESNET z.s.p.o.
*
* 4. Neither the name of the 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 __tile_h
#define __tile_h
struct video_frame;
struct codec_info_t;
enum codec_t;
struct tile_info {
unsigned int h_reserved:8;
unsigned int pos_x:4;
unsigned int pos_y:4;
unsigned int x_count:4;
unsigned int y_count:4;
unsigned int t_reserved:8;
} __attribute__((__packed__));
/**
* vf_split splits the frame into multiple tiles.
* Caller is responsible for allocating memory for all of these: out (to hold
* all elements), out elements and theirs data member to hold tile data.
*
* width must be divisible by x_count && heigth by y_count (!)
*
* @param out output video frames array
* the resulting matrix will be stored row-dominant
* @param src source video frame
* @param x_count number of columns
* @param y_count number of rows
* @param preallocate used for preallocating buffers because determining right
* size can be cumbersome. Anyway only .data are allocated.
*/
void vf_split(struct video_frame *out, struct video_frame *src,
unsigned int x_count, unsigned int y_count, int preallocate);
void vf_split_horizontal(struct video_frame *out, struct video_frame *src,
unsigned int y_count);
uint32_t hton_tileinfo2uint(struct tile_info tile_info);
struct tile_info ntoh_uint2tileinfo(uint32_t packed);
/**
* tileinfo_eq:
* compares count of tiles
*
* @param t1 first structure
* @param t2 second structure
* @return 0 if different
* !0 if equal
*/
int tileinfo_eq_count(struct tile_info t1, struct tile_info t2);
#endif

View File

@@ -129,6 +129,7 @@ tx_send(struct video_tx *tx, struct video_frame *frame, struct rtp *rtp_session)
payload_hdr.colorspc = frame->color_spec;
payload_hdr.fps = htonl((int)(frame->fps * 65536));
payload_hdr.aux = htonl(frame->aux);
payload_hdr.tileinfo = hton_tileinfo2uint(frame->tile_info);
do {
payload_hdr.offset = htonl(pos);

View File

@@ -79,6 +79,8 @@ struct testcard_state {
SDL_Surface *surface;
struct timeval t0;
struct video_frame frame;
struct video_frame *tiles;
int last_tile_sent;
};
const int rect_colors[] = {
@@ -255,10 +257,37 @@ void toR10k(unsigned char *in, unsigned int width, unsigned int height)
}
}
static int configure_tiling(struct testcard_state *s, const char *fmt)
{
char *tmp, *token, *saveptr;
if(fmt[1] != '=') return 1;
s->frame.aux |= AUX_TILED;
tmp = strdup(&fmt[2]);
token = strtok_r(tmp, "x", &saveptr);
s->frame.tile_info.x_count = atoi(token);
token = strtok_r(NULL, "x", &saveptr);
s->frame.tile_info.y_count = atoi(token);
free(tmp);
if(s->frame.tile_info.x_count < 1u ||
s->frame.tile_info.y_count < 1u) {
fprintf(stderr, "There must be at least 1x1 tile.\n");
return 1;
}
s->tiles = (struct video_frame *)
malloc(s->frame.tile_info.x_count *
s->frame.tile_info.y_count *
sizeof(struct video_frame));
vf_split(s->tiles, &s->frame, s->frame.tile_info.x_count,
s->frame.tile_info.y_count, 1); /*prealloc*/
return 0;
}
void *vidcap_testcard_init(char *fmt)
{
struct testcard_state *s;
char *filename;
const char *strip_fmt = NULL;
FILE *in;
struct stat sb;
unsigned int i, j;
@@ -268,8 +297,9 @@ void *vidcap_testcard_init(char *fmt)
if (strcmp(fmt, "help") == 0) {
printf("testcard options:\n");
printf("\twidth:height:fps:codec[:filename][:p}\n");
printf("\twidth:height:fps:codec[:filename][:p][:s=XxY]\n");
printf("\tp - pan with frame\n");
printf("\ts - split the frames into XxY separate tiles\n");
show_codec_help("testcard");
return NULL;
}
@@ -341,7 +371,8 @@ void *vidcap_testcard_init(char *fmt)
s->size = aligned_x * s->frame.height * bpp;
filename = strtok(NULL, ":");
if (filename && strcmp(filename, "p") != 0) {
if (filename && strcmp(filename, "p") != 0
&& strncmp(filename, "s=", 2ul) != 0) {
s->frame.data = malloc(s->size);
if (stat(filename, &sb)) {
perror("stat");
@@ -375,8 +406,11 @@ void *vidcap_testcard_init(char *fmt)
SDL_CreateRGBSurface(SDL_SWSURFACE, aligned_x, s->frame.height,
32, 0xff, 0xff00, 0xff0000,
0xff000000);
if (filename && filename[0] == 'p') {
s->pan = 48;
if (filename) {
if(filename[0] == 'p')
s->pan = 48;
else if(filename[0] == 's')
strip_fmt = filename;
}
for (j = 0; j < s->frame.height; j += rect_size) {
@@ -429,6 +463,14 @@ void *vidcap_testcard_init(char *fmt)
if (tmp) {
if (tmp[0] == 'p') {
s->pan = 48;
} else if (tmp[0] == 's') {
strip_fmt = tmp;
}
}
tmp = strtok(NULL, ":");
if (tmp) {
if (tmp[0] == 's') {
strip_fmt = tmp;
}
}
@@ -440,6 +482,13 @@ void *vidcap_testcard_init(char *fmt)
s->frame.state = s;
s->frame.data_len = s->size;
if(strip_fmt != NULL) {
if(configure_tiling(s, strip_fmt) != 0)
return NULL;
} else {
s->frame.aux &= ~AUX_TILED;
}
return s;
}
@@ -450,6 +499,14 @@ void vidcap_testcard_done(void *state)
free(s->frame.data);
if (s->surface)
SDL_FreeSurface(s->surface);
if (s->frame.aux & AUX_TILED)
{
unsigned int i;
for (i = 0u; i < s->frame.tile_info.x_count *
s->frame.tile_info.y_count; ++i)
free(s->tiles[i].data);
free(s->tiles);
}
free(s);
}
@@ -460,6 +517,12 @@ struct video_frame *vidcap_testcard_grab(void *arg)
state = (struct testcard_state *)arg;
if (state->frame.aux & AUX_TILED) {
if (state->last_tile_sent < state->tiles->tile_info.x_count *
state->tiles->tile_info.y_count - 1)
return &state->tiles[++state->last_tile_sent];
}
gettimeofday(&curr_time, NULL);
if (tv_diff(curr_time, state->last_frame_time) >
1.0 / (double)state->frame.fps) {
@@ -499,6 +562,16 @@ struct video_frame *vidcap_testcard_grab(void *arg)
}
}
#endif
if (state->frame.aux & AUX_TILED && state->last_tile_sent ==
state->tiles->tile_info.x_count *
state->tiles->tile_info.y_count
- 1) {
vf_split(state->tiles, &state->frame,
state->frame.tile_info.x_count,
state->frame.tile_info.y_count, 0);
state->last_tile_sent = 0;
return state->tiles;
}
return &state->frame;
}
return NULL;

View File

@@ -55,11 +55,11 @@
#include "video_codec.h"
const struct codec_info_t codec_info[] = {
{RGBA, "RGBA", 0, 0, 4.0, 1},
{UYVY, "UYVY", 846624121, 0, 2, 0},
{Vuy2, "2vuy", '2vuy', 0, 2, 0},
{DVS8, "DVS8", 0, 0, 2, 0},
{R10k, "R10k", 1378955371, 0, 4, 1},
{RGBA, "RGBA", 0, 1, 4.0, 1},
{UYVY, "UYVY", 846624121, 1, 2, 0},
{Vuy2, "2vuy", '2vuy', 1, 2, 0},
{DVS8, "DVS8", 0, 1, 2, 0},
{R10k, "R10k", 1378955371, 1, 4, 1},
{v210, "v210", 1983000880, 48, 8.0 / 3.0, 0},
{DVS10, "DVS10", 0, 48, 8.0 / 3.0, 0},
{0, NULL, 0, 0, 0.0, 0}
@@ -406,3 +406,4 @@ void vc_copylineDVS10(unsigned char *dst, unsigned char *src, int src_len)
}
#endif /* !(HAVE_MACOSX || HAVE_32B_LINUX) */

View File

@@ -47,6 +47,7 @@
#ifndef __video_codec_h
#define __video_codec_h
#include "tile.h"
typedef enum {
RGBA,
@@ -59,7 +60,11 @@ typedef enum {
} codec_t;
typedef void (*decoder_t)(unsigned char *dst, unsigned char *src, int dst_len, int rshift, int gshift, int bshift);
typedef void (*reconfigure_t)(void *state, int width, int height, codec_t color_spec, double fps, int aux);
typedef void (*reconfigure_t)(void *state, int width, int height, codec_t color_spec, double fps, int aux, struct tile_info tileinfo);
/**
* function of this type should return buffer corresponding to the given tile_info struct
*/
typedef struct video_frame * (*get_tile_buffer_t)(void *state, struct tile_info tile_info);
struct video_frame {
@@ -74,7 +79,7 @@ struct video_frame {
unsigned int dst_linesize; /* framebuffer pitch */
unsigned int dst_pitch; /* framebuffer pitch - it can be larger if SDL resolution is larger than data */
unsigned int src_linesize; /* display data pitch */
unsigned int dst_x_offset; /* X offset in frame buffer */
unsigned int dst_x_offset; /* X offset in frame buffer in bytes */
double src_bpp;
double dst_bpp;
int rshift;
@@ -82,9 +87,11 @@ struct video_frame {
int bshift;
decoder_t decoder;
reconfigure_t reconfigure;
get_tile_buffer_t get_tile_buffer;
void *state;
double fps;
int aux;
struct tile_info tile_info;
};
@@ -117,6 +124,7 @@ void vc_copylineDVS10toV210(unsigned char *dst, unsigned char *src, int dst_len)
#define AUX_RGB 1<<3 /* if device supports both, set both */
#define AUX_YUV 1<<4
#define AUX_10Bit 1<<5
#define AUX_TILED 1<<6
#endif

View File

@@ -96,6 +96,7 @@ struct state_sdl {
SDL_Overlay *yuv_image;
SDL_Surface *rgb_image;
struct video_frame frame;
struct video_frame *tiles;
pthread_t thread_id;
SDL_sem *semaphore;
@@ -127,7 +128,8 @@ void deinterlace(struct state_sdl *s, unsigned char *buffer);
static void show_help(void);
void cleanup_screen(struct state_sdl *s);
void reconfigure_screen(void *s, unsigned int width, unsigned int height,
codec_t codec, double fps, int aux);
codec_t codec, double fps, int aux, struct tile_info tile_info);
static struct video_frame * get_tile_buffer(void *s, struct tile_info tile_info);
extern int should_exit;
@@ -247,11 +249,15 @@ static void loadSplashscreen(struct state_sdl *s) {
static void toggleFullscreen(struct state_sdl *s) {
if(s->fs) {
s->fs = 0;
reconfigure_screen(s, s->frame.width, s->frame.height, s->codec_info->codec, s->frame.fps, s->frame.aux);
reconfigure_screen(s, s->frame.width, s->frame.height,
s->codec_info->codec, s->frame.fps,
s->frame.aux, s->frame.tile_info);
}
else {
s->fs = 1;
reconfigure_screen(s, s->frame.width, s->frame.height, s->codec_info->codec, s->frame.fps, s->frame.aux);
reconfigure_screen(s, s->frame.width, s->frame.height,
s->codec_info->codec, s->frame.fps,
s->frame.aux, s->frame.tile_info);
}
}
@@ -369,7 +375,8 @@ void cleanup_screen(struct state_sdl *s)
void
reconfigure_screen(void *state, unsigned int width, unsigned int height,
codec_t color_spec, double fps, int aux)
codec_t color_spec, double fps, int aux,
struct tile_info tile_info)
{
struct state_sdl *s = (struct state_sdl *)state;
int itemp;
@@ -383,13 +390,19 @@ reconfigure_screen(void *state, unsigned int width, unsigned int height,
cleanup_screen(s);
fprintf(stdout, "Reconfigure to size %dx%d\n", width, height);
s->frame.width = width;
s->frame.height = height;
if (aux & AUX_TILED) {
s->frame.width = width * tile_info.x_count;
s->frame.height = height * tile_info.y_count;
} else {
s->frame.width = width;
s->frame.height = height;
}
s->frame.fps = fps;
s->frame.aux = aux;
fprintf(stdout, "Reconfigure to size %dx%d\n", s->frame.width,
s->frame.height);
ret =
XGetGeometry(s->display, DefaultRootWindow(s->display), &wtemp,
&itemp, &itemp, &x_res_x, &x_res_y, &utemp, &utemp);
@@ -478,10 +491,10 @@ reconfigure_screen(void *state, unsigned int width, unsigned int height,
(int) s->sdl_screen->pitch * x_res_y -
s->sdl_screen->pitch * s->dst_rect.y +
s->dst_rect.x * s->sdl_screen->format->BytesPerPixel;
s->frame.dst_x_offset =
s->dst_rect.x * s->sdl_screen->format->BytesPerPixel;
s->frame.dst_bpp = s->sdl_screen->format->BytesPerPixel;
s->frame.dst_pitch = s->sdl_screen->pitch;
s->frame.dst_x_offset =
s->dst_rect.x * s->sdl_screen->format->BytesPerPixel;
s->frame.dst_bpp = s->sdl_screen->format->BytesPerPixel;
s->frame.dst_pitch = s->sdl_screen->pitch;
} else {
s->frame.data = (char *)*s->yuv_image->pixels;
s->frame.data_len = s->frame.width * s->frame.height * 2;
@@ -509,14 +522,54 @@ reconfigure_screen(void *state, unsigned int width, unsigned int height,
break;
}
if (s->tiles != NULL) {
free(s->tiles);
s->tiles = NULL;
}
if (aux & AUX_TILED) {
int x, y;
const int x_cnt = tile_info.x_count;
s->frame.tile_info = tile_info;
s->tiles = (struct video_frame *)
malloc(s->frame.tile_info.x_count *
s->frame.tile_info.y_count *
sizeof(struct video_frame));
for (y = 0; y < s->frame.tile_info.y_count; ++y)
for(x = 0; x < s->frame.tile_info.x_count; ++x) {
memcpy(&s->tiles[y*x_cnt + x], &s->frame,
sizeof(struct video_frame));
s->tiles[y*x_cnt + x].width = width;
s->tiles[y*x_cnt + x].height = height;
s->tiles[y*x_cnt + x].tile_info.pos_x = x;
s->tiles[y*x_cnt + x].tile_info.pos_y = y;
s->tiles[y*x_cnt + x].dst_x_offset +=
x * width * (s->rgb ? 4 : 2 /*vuy2*/);
s->tiles[y*x_cnt + x].data +=
y * height * s->frame.dst_pitch;
s->tiles[y*x_cnt + x].src_linesize =
vc_getsrc_linesize(width, color_spec);
s->tiles[y*x_cnt + x].dst_linesize =
vc_getsrc_linesize((x + 1) * width, color_spec);
}
}
}
static struct video_frame * get_tile_buffer(void *state, struct tile_info tile_info) {
struct state_sdl *s = (struct state_sdl *)state;
assert(s->tiles != NULL); /* basic sanity test... */
return &s->tiles[tile_info.pos_x + tile_info.pos_y * tile_info.x_count];
}
void *display_sdl_init(char *fmt)
{
struct state_sdl *s;
int ret;
struct state_sdl *s;
int ret;
unsigned int i;
unsigned int i;
s = (struct state_sdl *)calloc(1, sizeof(struct state_sdl));
s->magic = MAGIC_SDL;
@@ -617,13 +670,18 @@ void *display_sdl_init(char *fmt)
return NULL;
}
s->frame.aux &= ~AUX_TILED; /* do not expect tiled video by default */
s->tiles = NULL;
if (fmt != NULL) {
reconfigure_screen(s, s->frame.width, s->frame.height, s->codec_info->codec, s->frame.fps, s->frame.aux);
reconfigure_screen(s, s->frame.width, s->frame.height,
s->codec_info->codec, s->frame.fps,
s->frame.aux, s->frame.tile_info);
loadSplashscreen(s);
}
s->frame.state = s;
s->frame.reconfigure = (reconfigure_t)reconfigure_screen;
s->frame.get_tile_buffer = (get_tile_buffer_t) get_tile_buffer;
if (pthread_create(&(s->thread_id), NULL,
display_thread_sdl, (void *)s) != 0) {
@@ -640,6 +698,8 @@ void display_sdl_done(void *state)
assert(s->magic == MAGIC_SDL);
if(s->tiles != NULL)
free(s->tiles);
/*FIXME: free all the stuff */
SDL_ShowCursor(SDL_ENABLE);