UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
ihdtv.c
Go to the documentation of this file.
1/*
2 * FILE: ihdtv/ihdtv.c
3 * AUTHORS: Martin Benes <martinbenesh@gmail.com>
4 * Lukas Hejtmanek <xhejtman@ics.muni.cz>
5 * Petr Holub <hopet@ics.muni.cz>
6 * Milos Liska <xliska@fi.muni.cz>
7 * Jiri Matela <matela@ics.muni.cz>
8 * Dalibor Matura <255899@mail.muni.cz>
9 * Ian Wesley-Smith <iwsmith@cct.lsu.edu>
10 *
11 * Copyright (c) 2005-2021 CESNET z.s.p.o.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, is permitted provided that the following conditions
15 * are met:
16 *
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 *
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in the
22 * documentation and/or other materials provided with the distribution.
23 *
24 * 3. All advertising materials mentioning features or use of this software
25 * must display the following acknowledgement:
26 *
27 * This product includes software developed by CESNET z.s.p.o.
28 *
29 * 4. Neither the name of CESNET nor the names of its contributors may be used
30 * to endorse or promote products derived from this software without specific
31 * prior written permission.
32 *
33 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
34 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
35 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
36 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
37 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
38 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
39 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
40 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
42 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
43 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
44 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45 *
46 *
47 */
48
49#ifdef HAVE_CONFIG_H
50#include "config.h"
51#include "config_unix.h"
52#include "config_win32.h"
53#endif // HAVE_CONFIG_H
54
55#ifndef _WIN32
56
57#include <stdio.h>
58#include <stdlib.h>
59#include <string.h>
60#include <errno.h>
61
62#include "ihdtv.h"
63#include "debug.h"
64#include "video.h"
65
66int
67ihdtv_init_rx_session(ihdtv_connection * connection, const char *address_1,
68 const char *address_2, unsigned int port1,
69 unsigned int port2, long mtu)
70{
71 connection->rx_port_1 = port1;
72 connection->rx_port_2 = port2;
73 connection->tx_port = 0;
74 connection->tx_socket_1 = 0;
75 connection->tx_socket_2 = 0;
76
77 connection->check_peer_ip = 0;
78
79 if ((connection->rx_socket_1 = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
80 fprintf(stderr, "Error creating first receiving socket");
81 exit(1);
82 }
83
84 if ((connection->rx_socket_2 = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
85 fprintf(stderr, "Error creating second receiving socket");
86 exit(1);
87 }
88
89 if (address_1 != NULL) // we want to accept packets only from certain address so we connect() the socket
90 {
91 struct hostent *target = gethostbyname(address_1); // FIXME: should free?
92 if (target == NULL) {
93 printf("Unknown target 1");
94 exit(1); // FIXME: maybe should just return but it requires not to return ihdtv_connection
95 }
96
97 connection->peer_address_1 = *(struct in_addr *)(void *) target->h_addr;
98
99 connection->check_peer_ip = 1;
100 }
101
102 if (address_2 != NULL) // we want to accept packets only from certain address so we connect() the socket
103 {
104 struct hostent *target = gethostbyname(address_2); // FIXME: should free?
105 if (target == NULL) {
106 printf("Unknown target 2");
107 exit(1); // FIXME: maybe should just return but it requires not to return ihdtv_connection
108 }
109
110 connection->peer_address_2 = *(struct in_addr *)(void *) target->h_addr;
111
112 connection->check_peer_ip = 1;
113 }
114
115 struct sockaddr_in rx_address;
116 rx_address.sin_family = AF_INET;
117 rx_address.sin_addr.s_addr = htonl(INADDR_ANY);
118 rx_address.sin_port = htons(connection->rx_port_1);
119 memset(rx_address.sin_zero, '\0', sizeof(rx_address.sin_zero));
120 if (bind
121 (connection->rx_socket_1, (struct sockaddr *)&rx_address,
122 sizeof(rx_address)) == -1) {
123 fprintf(stderr, "Error binding receiver to the first socket");
124 exit(1);
125 }
126
127 rx_address.sin_family = AF_INET;
128 rx_address.sin_addr.s_addr = htonl(INADDR_ANY);
129 rx_address.sin_port = htons(connection->rx_port_2);
130 memset(rx_address.sin_zero, '\0', sizeof(rx_address.sin_zero));
131 if (bind
132 (connection->rx_socket_2, (struct sockaddr *)&rx_address,
133 sizeof(rx_address)) == -1) {
134 fprintf(stderr, "Error binding receiver to the second socket");
135 exit(1);
136 }
137
138 connection->video_data_per_packet = mtu;
139 if (mtu == 0) {
140 fprintf(stderr, "Error: mtu must be > 0");
141 return -1;
142 }
143
144 connection->bytes_per_frame = 5529600;
145 connection->bytes_per_half_frame = connection->bytes_per_frame / 2;
146
147 // we will count the number of packets/frame and the size of the last packet
148 connection->video_packets_per_half_frame =
149 connection->bytes_per_half_frame /
150 connection->video_data_per_packet;
151 if (connection->bytes_per_half_frame % connection->video_data_per_packet) // we have the final packet of each half of the frame smaller than video_data_per_packet
152 {
153 connection->video_data_per_last_packet =
154 connection->bytes_per_half_frame %
155 connection->video_data_per_packet;
156 connection->video_packets_per_half_frame++;
157 } else {
158 connection->video_data_per_last_packet =
159 connection->video_data_per_packet;
160 }
161
162 connection->video_packets_per_frame =
163 2 * connection->video_packets_per_half_frame;
164// fprintf(stderr,"Video_packets_per_frame=%u, video_data_per_packet=%d, video_data_per_last_packet %d\n", connection->video_packets_per_frame, (int)connection->video_data_per_packet, connection->video_data_per_last_packet);
165
166/*
167 connection->video_packets_per_frame = 682;
168 connection->bytes_per_frame = 5529600; // 1920 * 1080 v210
169 connection->bytes_per_half_frame = connection->bytes_per_frame / 2;
170*/
171
172 connection->current_frame = 0;
173 connection->pending_packet = 0;
174
175 // set sockets to nonblocking(will utilize 100% of a CPU)
176 if (fcntl(connection->rx_socket_1, F_SETFL, O_NONBLOCK) < 0) {
177 fprintf(stderr, "Error setting rx_socket_1 as nonblocking\n");
178 exit(1);
179 }
180
181 if (fcntl(connection->rx_socket_2, F_SETFL, O_NONBLOCK) < 0) {
182 fprintf(stderr, "Error setting rx_socket_2 as nonblocking\n");
183 exit(1);
184 }
185
186 return 0;
187}
188
189int
190ihdtv_init_tx_session(ihdtv_connection * connection, const char *address_1,
191 const char *address_2, long mtu)
192{
193 connection->rx_port_1 = 0;
194 connection->rx_port_2 = 0;
195 connection->tx_port = 0; // not used so far
196 connection->tx_socket_1 = 0;
197 connection->tx_socket_2 = 0;
198
199 struct hostent *target_1 = gethostbyname(address_1);
200 if (target_1 == NULL) {
201 printf("Unknown target 1");
202 exit(1); // FIXME: maybe should just return but it requires not to return ihdtv_connection
203 }
204
205 struct hostent *target_2 = gethostbyname(address_2);
206 if (target_2 == NULL) {
207 printf("Unknown target 2");
208 exit(1); // FIXME: maybe should just return but it requires not to return ihdtv_connection
209 }
210
211 if ((connection->tx_socket_1 = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
212 fprintf(stderr, "Error creating sending socket 1");
213 exit(1);
214 }
215 if ((connection->tx_socket_2 = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
216 fprintf(stderr, "Error creating sending socket 2");
217 exit(1);
218 }
219
220 connection->target_sockaddress_1.sin_family = AF_INET;
221 connection->target_sockaddress_1.sin_port = htons(3000); // FIXME: target port
222 connection->target_sockaddress_1.sin_addr =
223 *((struct in_addr *)(void *) target_1->h_addr);
224 memset(connection->target_sockaddress_1.sin_zero, '\0',
225 sizeof(connection->target_sockaddress_1.sin_zero));
226 connection->target_sockaddress_2.sin_family = AF_INET;
227 connection->target_sockaddress_2.sin_port = htons(3001); // FIXME: target port
228 connection->target_sockaddress_2.sin_addr =
229 *((struct in_addr *)(void *) target_2->h_addr);
230 memset(connection->target_sockaddress_2.sin_zero, '\0',
231 sizeof(connection->target_sockaddress_2.sin_zero));
232
233 connection->video_data_per_packet = mtu;
234 if (mtu == 0) {
235 fprintf(stderr, "Error: mtu must be > 0");
236 return -1;
237 }
238
239 connection->bytes_per_frame = 5529600;
240 connection->bytes_per_half_frame = connection->bytes_per_frame / 2;
241
242 // we will count the number of packets/frame and the size of the last packet
243 connection->video_packets_per_half_frame =
244 connection->bytes_per_half_frame /
245 connection->video_data_per_packet;
246 if (connection->bytes_per_half_frame % connection->video_data_per_packet) // we have the final packet of each half of the frame smaller than video_data_per_packet
247 {
248 connection->video_data_per_last_packet =
249 connection->bytes_per_half_frame %
250 connection->video_data_per_packet;
251 connection->video_packets_per_half_frame++;
252 } else {
253 connection->video_data_per_last_packet =
254 connection->video_data_per_packet;
255 }
256
257 connection->video_packets_per_frame =
258 2 * connection->video_packets_per_half_frame;
259
260// fprintf(stderr,"Video_packets_per_frame=%u, video_data_per_packet=%d, video_data_per_last_packet %d\n", connection->video_packets_per_frame, (int)connection->video_data_per_packet, connection->video_data_per_last_packet);
261
262 connection->current_frame = 0;
263 connection->pending_packet = 0;
264
265 return 0;
266}
267
268int
269ihdtv_send(ihdtv_connection * connection, struct video_frame *tx_frame,
270 unsigned long buffer_length)
271{
272 UNUSED(buffer_length);
273 struct msghdr msg1, msg2;
274
275 msg1.msg_name = &connection->target_sockaddress_1;
276 msg2.msg_name = &connection->target_sockaddress_2;
277
278 msg1.msg_namelen = sizeof(connection->target_sockaddress_1);
279 msg2.msg_namelen = sizeof(connection->target_sockaddress_2);
280
281 // header + data
282 struct iovec vector1[2];
283 struct iovec vector2[2];
284
285 /* TODO: is this correct ?? */
286 msg1.msg_iov = (struct iovec *) &vector1;
287 msg2.msg_iov = (struct iovec *) &vector2;
288
289 msg1.msg_iovlen = 2;
290 msg2.msg_iovlen = 2;
291
292 msg1.msg_control = 0;
293 msg2.msg_control = 0;
294 msg1.msg_controllen = 0;
295 msg2.msg_controllen = 0;
296 msg1.msg_flags = 0;
297 msg2.msg_flags = 0;
298
299 // only used for headers
300 ihdtv_packet packet_1;
301 ihdtv_packet packet_2;
302
303 packet_1.frame_number = connection->current_frame;
304 packet_2.frame_number = connection->current_frame;
305
306 packet_1.stream_id = 0; // top half
307 packet_2.stream_id = 1; // bottom half
308
309 unsigned int offset_absolut_1 = 0; // offset to buffer ( in bytes )
310 unsigned int offset_absolut_2 = connection->bytes_per_half_frame;
311
312 unsigned int packet_relative_offset = 0; // this is the number we put into buffer
313
314 unsigned int i;
315 for (i = 0; i < connection->video_packets_per_half_frame - 1; i++) {
316 packet_1.offset = packet_relative_offset;
317 vector1[0].iov_base = &packet_1;
318 vector1[0].iov_len = 16; // we only use the header
319 vector1[1].iov_base = vf_get_tile(tx_frame, 0)->data + offset_absolut_1;
320 vector1[1].iov_len = connection->video_data_per_packet;
321 if (sendmsg(connection->tx_socket_1, &msg1, 0) == -1) {
322 perror("Sending data to address 1");
323 exit(1);
324 }
325
326 packet_2.offset = packet_relative_offset;
327 vector2[0].iov_base = &packet_2;
328 vector2[0].iov_len = 16; // we only use the header
329 vector2[1].iov_base = vf_get_tile(tx_frame, 0)->data + offset_absolut_2;
330 vector2[1].iov_len = connection->video_data_per_packet;
331 if (sendmsg(connection->tx_socket_2, &msg2, 0) == -1) {
332 perror("Sending data to address 2");
333 exit(1);
334 }
335
336 ++packet_relative_offset;
337 offset_absolut_1 += connection->video_data_per_packet;
338 offset_absolut_2 += connection->video_data_per_packet;
339 }
340
341 packet_1.offset = packet_relative_offset;
342 vector1[0].iov_base = &packet_1;
343 vector1[0].iov_len = 16; // we only use the header
344 vector1[1].iov_base = vf_get_tile(tx_frame, 0)->data + offset_absolut_1;
345 vector1[1].iov_len = connection->video_data_per_last_packet;
346 if (sendmsg(connection->tx_socket_1, &msg1, 0) == -1) {
347 perror("Sending data to address 1");
348 exit(1);
349 }
350
351 packet_2.offset = packet_relative_offset;
352 vector2[0].iov_base = &packet_2;
353 vector2[0].iov_len = 16; // we only use the header
354 vector2[1].iov_base = vf_get_tile(tx_frame, 0)->data + offset_absolut_2;
355 vector2[1].iov_len = connection->video_data_per_last_packet;
356 if (sendmsg(connection->tx_socket_2, &msg2, 0) == -1) {
357 perror("Sending data to address 2");
358 exit(1);
359 }
360
361 ++(connection->current_frame);
362
363 return 0;
364}
365
366inline static int packet_to_buffer(const ihdtv_connection * connection, char *buffer, const unsigned long buffer_length, const ihdtv_packet * packet, const int packet_length) // returns number of written packets (1 or 0)
367{
368 if (buffer_length < (unsigned long int)
369 (packet->offset * connection->video_data_per_packet +
370 (packet_length - 16)))
371 return 0;
372
373 if (packet->stream_id == 0) {
374 memcpy(buffer +
375 (packet->offset) * connection->video_data_per_packet,
376 packet->data, packet_length - 16);
377 return 1;
378 }
379
380 if (packet->stream_id == 1) {
381 memcpy(buffer +
382 (packet->offset) * connection->video_data_per_packet +
383 connection->bytes_per_half_frame, packet->data,
384 packet_length - 16);
385 return 1;
386 }
387
388 return 0;
389}
390
391int
392ihdtv_receive(ihdtv_connection * connection, char *buffer,
393 const unsigned long buffer_length)
394{
395 int packets_number = 0;
396
397 ihdtv_packet packet;
398 int num_bytes;
399 struct sockaddr_in sender_address; // where did the packets come from
400 socklen_t sender_address_length = sizeof(sender_address);
401
402 int dont_check_ip = !(connection->check_peer_ip);
403
404 if (buffer == NULL) {
405 fprintf(stderr,
406 "iHDTV receiver: buffer is empty, not receiving.\n");
407 return -1;
408 }
409// memset(buffer, 0, buffer_length); // make holes in frame more visible -- your computer might not be fast enough for this, if you experience performance problems, try to comment this out
410
411 // if we have some data from previous session, we use it
412 if (connection->pending_packet) {
413 packet_to_buffer(connection, buffer, buffer_length,
414 &(connection->ppacket),
415 connection->ppacket_size);
416// printf("processing pending packet %ld\n", connection->ppacket.frame_number); // FIXME: delete this
417
418 connection->pending_packet = 0;
419 ++packets_number;
420 }
421
422 while (1) {
423 if ((num_bytes =
424 recvfrom(connection->rx_socket_1, &packet, sizeof(packet),
425 0, (struct sockaddr *)&sender_address,
426 &sender_address_length)) > -1) {
427 if ((dont_check_ip)
428 || (connection->peer_address_1.s_addr ==
429 sender_address.sin_addr.s_addr)
430 || (connection->peer_address_2.s_addr ==
431 sender_address.sin_addr.s_addr)) {
432 if (packet.stream_id == 0 || packet.stream_id == 1) { // it is strange, but ihdtv seems to be sending video and audio with different frame numbers in some cases, so we only work with video here
433 if ((connection->current_frame < packet.frame_number) || (packet.frame_number + 10 < connection->current_frame)) // we just received frame we didn't expect
434 {
435// printf("current frame: %llu (packets: %u) incoming frame: %llu\n", connection->current_frame, packets_number, packet.frame_number);
436 connection->current_frame =
437 packet.frame_number;
438 connection->pending_packet++;
439 connection->ppacket = packet;
440 connection->ppacket_size =
441 num_bytes;
442
443 break;
444 }
445
446 if (packet.frame_number ==
447 connection->current_frame)
448 packets_number +=
449 packet_to_buffer(connection,
450 buffer,
451 buffer_length,
452 &packet,
453 num_bytes);
454
455 if ((unsigned int) packets_number == connection->video_packets_per_frame) // we have all packets of a frame
456 {
457 connection->current_frame++;
458 break;
459 }
460 }
461 }
462
463 } else if (errno != EWOULDBLOCK) {
464 fprintf(stderr, "Error receiving packet\n");
465 exit(1);
466 }
467
468 if ((num_bytes =
469 recvfrom(connection->rx_socket_2, &packet, sizeof(packet),
470 0, (struct sockaddr *)&sender_address,
471 &sender_address_length)) > -1) {
472 if ((dont_check_ip)
473 || (connection->peer_address_1.s_addr ==
474 sender_address.sin_addr.s_addr)
475 || (connection->peer_address_2.s_addr ==
476 sender_address.sin_addr.s_addr)) {
477 if (packet.stream_id == 0 || packet.stream_id == 1) { // it is strange, but ihdtv seems to be sending video and audio with different frame numbers in some cases, so we only work with video here
478 if ((connection->current_frame < packet.frame_number) || (packet.frame_number + 10 < connection->current_frame)) // we just received frame we didn't expect
479 {
480// printf("current frame: %llu (packets: %u) incoming frame: %llu\n", connection->current_frame, packets_number, packet.frame_number);
481 connection->current_frame =
482 packet.frame_number;
483 connection->pending_packet++;
484 connection->ppacket = packet;
485 connection->ppacket_size =
486 num_bytes;
487
488 break;
489 }
490 if (packet.frame_number ==
491 connection->current_frame)
492 packets_number +=
493 packet_to_buffer(connection,
494 buffer,
495 buffer_length,
496 &packet,
497 num_bytes);
498
499 if ((unsigned int ) packets_number == connection->video_packets_per_frame) // we have all packets of a frame
500 {
501 connection->current_frame++;
502 break;
503 }
504 }
505 }
506
507 } else if (errno != EWOULDBLOCK) {
508 fprintf(stderr, "Error receiving packet\n");
509 exit(1);
510 }
511 }
512
513 return 0;
514}
515
516#endif // _WIN32
517
#define UNUSED(x)
int ihdtv_receive(ihdtv_connection *connection, char *buffer, const unsigned long buffer_length)
Definition ihdtv.c:392
int ihdtv_init_rx_session(ihdtv_connection *connection, const char *address_1, const char *address_2, unsigned int port1, unsigned int port2, long mtu)
Definition ihdtv.c:67
int ihdtv_init_tx_session(ihdtv_connection *connection, const char *address_1, const char *address_2, long mtu)
Definition ihdtv.c:190
int ihdtv_send(ihdtv_connection *connection, struct video_frame *tx_frame, unsigned long buffer_length)
Definition ihdtv.c:269
static int packet_to_buffer(const ihdtv_connection *connection, char *buffer, const unsigned long buffer_length, const ihdtv_packet *packet, const int packet_length)
Definition ihdtv.c:366
uint64_t current_frame
Definition ihdtv.h:85
int tx_socket_2
Definition ihdtv.h:98
unsigned int video_packets_per_frame
Definition ihdtv.h:83
unsigned long bytes_per_frame
Definition ihdtv.h:87
int tx_socket_1
Definition ihdtv.h:97
unsigned int ppacket_size
Definition ihdtv.h:102
struct sockaddr_in target_sockaddress_1
Definition ihdtv.h:106
long video_data_per_packet
Definition ihdtv.h:90
unsigned int rx_port_2
Definition ihdtv.h:79
int pending_packet
Definition ihdtv.h:100
unsigned int tx_port
Definition ihdtv.h:81
struct sockaddr_in target_sockaddress_2
Definition ihdtv.h:107
unsigned int rx_port_1
Definition ihdtv.h:78
struct in_addr peer_address_1
Definition ihdtv.h:110
int check_peer_ip
Definition ihdtv.h:109
struct in_addr peer_address_2
Definition ihdtv.h:111
ihdtv_packet ppacket
Definition ihdtv.h:101
unsigned long bytes_per_half_frame
Definition ihdtv.h:88
unsigned int video_packets_per_half_frame
Definition ihdtv.h:84
int rx_socket_1
Definition ihdtv.h:94
int rx_socket_2
Definition ihdtv.h:95
long video_data_per_last_packet
Definition ihdtv.h:91
char * data
Raw tile data. Pointer must be at least 4B aligned.
Definition types.h:287
Struct video_frame represents a video frame and contains video description.
Definition types.h:305
This is an umbrella header for video functions.
struct tile * vf_get_tile(struct video_frame *buf, int pos)
Returns pointer to n-th tile from video frame Equivalent to &video_frame::tiles[pos].