From 3c912d7f55567cc19f026fff19fc2ff702fc4786 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 28 Apr 2011 16:41:50 +0200 Subject: [PATCH] Added possibility to send throught multiple links + basis for 4K transmit --- ultragrid/Makefile.in | 1 + ultragrid/src/main.c | 152 +++++++++++++++++---- ultragrid/src/rtp/decoders.c | 42 +++--- ultragrid/src/rtp/rtp.c | 42 ++++++ ultragrid/src/rtp/rtp.h | 2 + ultragrid/src/rtp/rtp_callback.h | 1 + ultragrid/src/tile.c | 179 +++++++++++++++++++++++++ ultragrid/src/tile.h | 100 ++++++++++++++ ultragrid/src/transmit.c | 1 + ultragrid/src/video_capture/testcard.c | 81 ++++++++++- ultragrid/src/video_codec.c | 11 +- ultragrid/src/video_codec.h | 14 +- ultragrid/src/video_display/sdl.c | 92 ++++++++++--- 13 files changed, 651 insertions(+), 67 deletions(-) create mode 100644 ultragrid/src/tile.c create mode 100644 ultragrid/src/tile.h diff --git a/ultragrid/Makefile.in b/ultragrid/Makefile.in index ca49f8f67..3f36327da 100644 --- a/ultragrid/Makefile.in +++ b/ultragrid/Makefile.in @@ -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@ \ diff --git a/ultragrid/src/main.c b/ultragrid/src/main.c index ea76628c9..ef26b7f2d 100644 --- a/ultragrid/src/main.c +++ b/ultragrid/src/main.c @@ -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 ] [-t ] [-g ] [-m ] [-f ] [-c] [-p] [-i] [-b <8|10>] address\n\n"); + ("Usage: uv [-d ] [-t ] [-g ] [-m ] [-f ] [-c] [-p] [-i] [-b <8|10>] [-S] address(es)\n\n"); printf ("\t-d \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) diff --git a/ultragrid/src/rtp/decoders.c b/ultragrid/src/rtp/decoders.c index 4ae0c658b..286a94d7b 100644 --- a/ultragrid/src/rtp/decoders.c +++ b/ultragrid/src/rtp/decoders.c @@ -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 */ } diff --git a/ultragrid/src/rtp/rtp.c b/ultragrid/src/rtp/rtp.c index 8f2e2ee33..293ff0c70 100644 --- a/ultragrid/src/rtp/rtp.c +++ b/ultragrid/src/rtp/rtp.c @@ -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()) diff --git a/ultragrid/src/rtp/rtp.h b/ultragrid/src/rtp/rtp.h index d13552146..b262b82ad 100644 --- a/ultragrid/src/rtp/rtp.h +++ b/ultragrid/src/rtp/rtp.h @@ -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, diff --git a/ultragrid/src/rtp/rtp_callback.h b/ultragrid/src/rtp/rtp_callback.h index bf518ec8f..b196c875e 100644 --- a/ultragrid/src/rtp/rtp_callback.h +++ b/ultragrid/src/rtp/rtp_callback.h @@ -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 */ diff --git a/ultragrid/src/tile.c b/ultragrid/src/tile.c new file mode 100644 index 000000000..1510f52ae --- /dev/null +++ b/ultragrid/src/tile.c @@ -0,0 +1,179 @@ +/* + * FILE: tile.c + * AUTHORS: Martin Benes + * Lukas Hejtmanek + * Petr Holub + * Milos Liska + * Jiri Matela + * Dalibor Matura <255899@mail.muni.cz> + * Ian Wesley-Smith + * + * 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 +#include +#include +#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; +} + diff --git a/ultragrid/src/tile.h b/ultragrid/src/tile.h new file mode 100644 index 000000000..c7c378885 --- /dev/null +++ b/ultragrid/src/tile.h @@ -0,0 +1,100 @@ +/* + * FILE: tile.h + * AUTHORS: Martin Benes + * Lukas Hejtmanek + * Petr Holub + * Milos Liska + * Jiri Matela + * Dalibor Matura <255899@mail.muni.cz> + * Ian Wesley-Smith + * + * 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 diff --git a/ultragrid/src/transmit.c b/ultragrid/src/transmit.c index d402db353..06477b4e8 100644 --- a/ultragrid/src/transmit.c +++ b/ultragrid/src/transmit.c @@ -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); diff --git a/ultragrid/src/video_capture/testcard.c b/ultragrid/src/video_capture/testcard.c index 5c4966d4d..05d32713f 100644 --- a/ultragrid/src/video_capture/testcard.c +++ b/ultragrid/src/video_capture/testcard.c @@ -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; diff --git a/ultragrid/src/video_codec.c b/ultragrid/src/video_codec.c index ec6eb7c75..ae127dd21 100644 --- a/ultragrid/src/video_codec.c +++ b/ultragrid/src/video_codec.c @@ -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) */ + diff --git a/ultragrid/src/video_codec.h b/ultragrid/src/video_codec.h index 4bd73eaaf..5ff81e865 100644 --- a/ultragrid/src/video_codec.h +++ b/ultragrid/src/video_codec.h @@ -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 + diff --git a/ultragrid/src/video_display/sdl.c b/ultragrid/src/video_display/sdl.c index 2066730de..e90f84ede 100644 --- a/ultragrid/src/video_display/sdl.c +++ b/ultragrid/src/video_display/sdl.c @@ -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);