1 /*
2 libndi
3 Copyright (C) 2020 VideoLAN
4 
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9 
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 Lesser General Public License for more details.
14 
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19 
20 #define _POSIX_C_SOURCE 200809L
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <inttypes.h>
25 #include <time.h>
26 
27 #include <sys/types.h>
28 #ifdef _WIN32
29 #include <ws2tcpip.h>
30 #include <winsock2.h>
31 #else
32 #include <sys/socket.h>
33 #include <netdb.h>
34 #endif
35 
36 #ifdef HAVE_POLL
37 #include <poll.h>
38 #else
39 #include "compat/poll.c"
40 #endif
41 
42 #include <libavutil/common.h>
43 #include <libavutil/frame.h>
44 #include <libavutil/fifo.h>
45 
46 #include "libndi.h"
47 
48 uint8_t ndi_xortab[] = {
49   0x4e, 0x44, 0x49, 0xae, 0x2c, 0x20, 0xa9, 0x32, 0x30, 0x31, 0x37, 0x20,
50   0x4e, 0x65, 0x77, 0x54, 0x65, 0x6b, 0x2c, 0x20, 0x50, 0x72, 0x6f, 0x70,
51   0x72, 0x69, 0x65, 0x74, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x43, 0x6f,
52   0x6e, 0x66, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2e, 0x20,
53   0x59, 0x6f, 0x75, 0x20, 0x61, 0x72, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x76,
54   0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20,
55   0x74, 0x68, 0x65, 0x20, 0x4e, 0x44, 0x49, 0xae, 0x20, 0x53, 0x44, 0x4b,
56   0x20, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x61, 0x74, 0x20,
57   0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x65, 0x77, 0x2e, 0x74,
58   0x6b, 0x2f, 0x6e, 0x64, 0x69, 0x73, 0x64, 0x6b, 0x5f, 0x6c, 0x69, 0x63,
59   0x65, 0x6e, 0x73, 0x65, 0x2f, 0x00, 0x00, 0x00
60 };
61 
62 typedef struct ndi_message
63 {
64     uint8_t *buf;
65     int len;
66 } ndi_message;
67 
68 struct ndi_ctx
69 {
70     /* buffers */
71     AVFifoBuffer *fifo;
72 
73     int target_size;
74 
75     int socket_fd;
76 
77     ndi_message ndi_request[4];
78     int pending_requests;
79 
80     ndi_data_cb callback;
81     void *user_data;
82 
83     /* options */
84     char *ip;
85     char *port;
86 };
87 
88 /* Probably could merge scramble and unscramble */
ndi_scramble_type1(uint8_t * buf,int len,uint32_t seed)89 static void ndi_scramble_type1(uint8_t *buf, int len, uint32_t seed)
90 {
91     uint64_t seed64 = ((uint64_t)seed << 32) | seed;
92     uint64_t seed1  = seed64 ^ 0xb711674bd24f4b24ULL;
93     uint64_t seed2  = seed64 ^ 0xb080d84f1fe3bf44ULL;
94 
95     if(len > 7) {
96         uint64_t *buf64 = (uint64_t*)buf;
97         int qwords = len / 8;
98         uint64_t tmp1 = seed1;
99         for(int i = 0; i < qwords; i++) {
100             seed1 = seed2;
101             tmp1 ^= (tmp1 << 23);
102             tmp1  = ((seed1 >> 9 ^ tmp1) >> 17) ^ tmp1 ^ seed1;
103             seed2 = tmp1 ^ buf64[i];
104             buf64[i] ^= tmp1 + seed1;
105             tmp1 = seed1;
106         }
107 
108         buf  = buf + qwords * 8;
109         len -= qwords * 8;
110     }
111 
112     if(len) {
113         uint64_t remainder = 0;
114         memcpy(&remainder, buf, len);
115         seed1 ^= seed1 << 23;
116         seed1 = ((seed2 >> 9 ^ seed1) >> 17) ^ seed1 ^ seed2;
117         remainder ^= seed1 + seed2;
118         memcpy(buf, &remainder, len);
119     }
120 }
121 
ndi_unscramble_type1(uint8_t * buf,int len,uint32_t seed)122 static void ndi_unscramble_type1(uint8_t *buf, int len, uint32_t seed)
123 {
124     uint64_t seed64 = ((uint64_t)seed << 32) | seed;
125     uint64_t seed1  = seed64 ^ 0xb711674bd24f4b24ULL;
126     uint64_t seed2  = seed64 ^ 0xb080d84f1fe3bf44ULL;
127 
128     if(len > 7) {
129         uint64_t *buf64 = (uint64_t*)buf;
130         int qwords = len / 8;
131         uint64_t tmp1 = seed1;
132         for(int i = 0; i < qwords; i++) {
133             seed1 = seed2;
134             tmp1 ^= (tmp1 << 23);
135             tmp1  = ((seed1 >> 9 ^ tmp1) >> 17) ^ tmp1 ^ seed1;
136             buf64[i] ^= tmp1 + seed1;
137             seed2 = tmp1 ^ buf64[i];
138             tmp1 = seed1;
139         }
140 
141         buf  = buf + qwords * 8;
142         len -= qwords * 8;
143     }
144 
145     if(len) {
146         uint64_t remainder = 0;
147         memcpy(&remainder, buf, len);
148         seed1 ^= seed1 << 23;
149         seed1 = ((seed2 >> 9 ^ seed1) >> 17) ^ seed1 ^ seed2;
150         remainder ^= seed1 + seed2;
151         memcpy(buf, &remainder, len);
152     }
153 }
154 
ndi_unscramble_type2(uint8_t * buf,int len,uint32_t seed)155 static void ndi_unscramble_type2(uint8_t *buf, int len, uint32_t seed)
156 {
157     int xor_len = 128;
158 
159     if(len >= 8) {
160         uint64_t *buf64 = (uint64_t*)buf;
161         int len8 = len >> 3;
162         int64_t tmp;
163         for(int i = 0; i < len8; i++) {
164             tmp = seed;
165             seed = buf64[i] & 0xffffffff;
166             buf64[i] = ((tmp * len * -0x61c8864680b583ebLL + 0xc42bd7dee6270f1bLL) ^ buf64[i]) * -0xe217c1e66c88cc3LL + 0x2daa8c593b1b4591LL;
167         }
168     }
169 
170     if(len < xor_len)
171         xor_len = len;
172 
173     for(int i = 0; i < xor_len; i++)
174         buf[i] ^= ndi_xortab[i];
175 }
176 
create_text_message(uint8_t * dst,int * dst_len,char * payload,int payload_len)177 static void create_text_message(uint8_t *dst, int *dst_len, char *payload, int payload_len)
178 {
179     /* Version/Scrambling type */
180     dst[0] = 0x01;
181     dst[1] = 0x80;
182 
183     /* Message Type */
184     dst[2] = NDI_DATA_TEXT;
185     dst[3] = 0;
186 
187     /* Header Length */
188     dst[4] = 8;
189     dst[5] = 0;
190     dst[6] = 0;
191     dst[7] = 0;
192 
193     /* Payload Length */
194     dst[8] = payload_len;
195     dst[9] = 0;
196     dst[10] = 0;
197     dst[11] = 0;
198 
199     /* 8 bytes of zero */
200     memset(&dst[12], 0, 8);
201 
202     /* Copy payload */
203     memcpy(&dst[20], payload, payload_len);
204 
205     *dst_len = 20 + payload_len;
206 }
207 
process_video_message(ndi_ctx * ndi_ctx,uint8_t * data,int header_len,int payload_len)208 static int process_video_message(ndi_ctx *ndi_ctx, uint8_t *data, int header_len, int payload_len)
209 {
210     ndi_data ndi_data = {0};
211 
212     uint32_t fourcc = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
213     uint32_t width  = (data[7] << 24) | (data[6] << 16) | (data[5] << 8) | data[4];
214     uint32_t height = (data[11] << 24) | (data[10] << 16) | (data[9] << 8) | data[8];
215     uint32_t fps_num = (data[15] << 24) | (data[14] << 16) | (data[13] << 8) | data[12];
216     uint32_t fps_den = (data[19] << 24) | (data[18] << 20) | (data[17] << 8) | data[16];
217 
218     // XXX: some more things in the header
219 
220     ndi_data.data_type = NDI_DATA_VIDEO;
221     ndi_data.data = data+header_len;
222     ndi_data.len = payload_len;
223 
224     ndi_data.fourcc = fourcc;
225     ndi_data.fps_num = fps_num;
226     ndi_data.fps_den = fps_den;
227     ndi_data.width = width;
228     ndi_data.height = height;
229 
230     ndi_ctx->callback(&ndi_data, ndi_ctx->user_data);
231 
232     return 0;
233 }
234 
process_audio_message(ndi_ctx * ndi_ctx,uint8_t * data,int header_len,int payload_len)235 static int process_audio_message(ndi_ctx *ndi_ctx, uint8_t *data, int header_len, int payload_len)
236 {
237     (void)payload_len; // XXX: Why is this unused?
238 
239     int ret = 0;
240     ndi_data ndi_data = {0};
241     uint32_t fourcc = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
242     uint32_t samples  = (data[7] << 24) | (data[6] << 16) | (data[5] << 8) | data[4];
243     uint32_t num_channels = (data[11] << 24) | (data[10] << 16) | (data[9] << 8) | data[8];
244     uint32_t sample_rate = (data[15] << 24) | (data[14] << 16) | (data[13] << 8) | data[12];
245     float scale_factors[16];
246     uint32_t num_nonzero_channels = 0;
247     uint16_t bps = sizeof(int16_t);
248 
249     // XXX: some more things in the header
250     data += header_len;
251 
252     if(fourcc == MKTAG('f','o','w','t')) {
253         bps = sizeof(float);
254         for(uint32_t i = 0; i < num_channels; i++) {
255             uint32_t tmp = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
256             memcpy(&scale_factors[i], &tmp, sizeof(float));
257             if(scale_factors[i] != 0.0f)
258                 num_nonzero_channels++;
259 
260             data += sizeof(float);
261         }
262     }
263     else if(fourcc == MKTAG('s','o','w','t')) {
264         for(uint32_t i = 0; i < num_channels; i++) {
265             scale_factors[i] = 1.0f;
266         }
267         num_nonzero_channels = num_channels;
268     }
269 
270     // XXX: Bounds check audio samples
271 
272     ndi_data.data_type = NDI_DATA_AUDIO;
273     ndi_data.fourcc = fourcc;
274     ndi_data.samples = samples;
275     ndi_data.num_channels = num_channels;
276     ndi_data.sample_rate = sample_rate;
277 
278     for(uint32_t i = 0; i < num_channels; i++) {
279         ndi_data.buf[i] = av_buffer_alloc(ndi_data.num_channels * ndi_data.samples * bps);
280         if(!ndi_data.buf[i]) {
281             ret = -1;
282             goto end;
283         }
284     }
285 
286     for(uint32_t j = 0; j < samples; j++) {
287         for(uint32_t i = 0; i < num_channels; i++) {
288             if(scale_factors[i] == 0.0f)
289                 memset(&ndi_data.buf[i]->data[4*j], 0, bps);
290             else {
291                 if(bps == 2) {
292                     ndi_data.buf[i]->data[2*j+0] = data[1];
293                     ndi_data.buf[i]->data[2*j+1] = data[0];
294                 } else if(bps == 4) {
295                     float sf = scale_factors[i] / 32767.0f;
296                     int16_t sample = ((uint16_t)data[1] << 8) | data[0];
297                     sf *= sample;
298                     memcpy(&ndi_data.buf[i]->data[4*j], &sf, sizeof(sf));
299                 }
300                 data += sizeof(int16_t);
301             }
302         }
303     }
304 
305     ndi_ctx->callback(&ndi_data, ndi_ctx->user_data);
306 
307 end:
308     for(uint32_t i = 0; i < num_channels; i++)
309         av_buffer_unref(&ndi_data.buf[i]);
310 
311     return ret;
312 }
313 
test_scramblev1(void)314 static void test_scramblev1(void)
315 {
316     srand( time(NULL) );
317     uint8_t buf[23], buf2[23];
318     uint32_t seed = rand();
319 
320     /* Generate random numbers */
321     for(size_t i = 0; i < sizeof(buf); i++)
322         buf[i] = rand();
323 
324     memcpy(buf2, buf, sizeof(buf));
325     ndi_scramble_type1(buf, sizeof(buf), seed);
326     ndi_unscramble_type1(buf, sizeof(buf), seed);
327 
328     int ret = memcmp(buf, buf2, sizeof(buf));
329 
330     if(ret)
331         fprintf(stderr, "scrambling mismatch \n");
332 }
333 
process_ndi_packet(ndi_ctx * ndi_ctx,uint8_t * data,int len)334 static void process_ndi_packet(ndi_ctx *ndi_ctx, uint8_t *data, int len)
335 {
336     uint32_t seed;
337     (void)len; // FIXME: Actually check length properly!
338 
339     /* MSB = scrambled bit */
340     uint16_t header_type = data[0] | (data[1] << 8);
341 
342     uint16_t message_type = data[2] | (data[3] << 8);
343     uint8_t scrambling_type = 1;
344 
345     uint32_t header_size = data[4] | (data[5] << 8) | (data[6] << 16) | (data[7] << 24);
346     uint32_t payload_len = data[8] | (data[9] << 8) | (data[10] << 16) | (data[11] << 24);
347     seed = header_size + payload_len;
348 
349     if(message_type == NDI_DATA_VIDEO && header_type > 3)
350         scrambling_type = 2;
351     else if(message_type == NDI_DATA_AUDIO && header_type > 2)
352         scrambling_type = 2;
353     else if(message_type == NDI_DATA_TEXT && header_type > 2)
354         scrambling_type = 2;
355 
356     if(scrambling_type == 1){
357         if(message_type == NDI_DATA_TEXT)
358             ndi_unscramble_type1(data+12, seed, seed);
359         else
360             ndi_unscramble_type1(data+12, header_size, seed);
361     } else {
362         if(message_type == NDI_DATA_TEXT)
363             ndi_unscramble_type2(data+12, seed, seed);
364         else
365             ndi_unscramble_type2(data+12, header_size, seed);
366     }
367 
368     int data_len = 12 + header_size + payload_len;
369     //printf("process %i %u %u %u %u \n", data_len, header_size, payload_len, message_type, scrambling_type);
370 
371     if(message_type == NDI_DATA_VIDEO)
372         process_video_message(ndi_ctx, data+12, header_size, payload_len);
373     else if(message_type == NDI_DATA_AUDIO)
374         process_audio_message(ndi_ctx, data+12, header_size, payload_len);
375 }
376 
handle_ndi_packet(ndi_ctx * ndi_ctx)377 static int handle_ndi_packet(ndi_ctx *ndi_ctx)
378 {
379     if(!ndi_ctx->target_size) {
380         uint8_t data[12];
381         av_fifo_generic_peek(ndi_ctx->fifo, data, 12, NULL); // fixme
382 
383         /* MSB = scrambled bit */
384         uint16_t header_type = data[0] | (data[1] << 8);
385 
386         if(header_type >> 15) {
387             header_type &= 0x7fff;
388             uint16_t message_type = data[2] | (data[3] << 8);
389 
390             uint32_t header_size = data[4] | (data[5] << 8) | (data[6] << 16) | (data[7] << 24);
391             uint32_t payload_len = data[8] | (data[9] << 8) | (data[10] << 16) | (data[11] << 24);
392             ndi_ctx->target_size = 12 + header_size + payload_len;
393             //printf("%u %u %u \n", header_size, payload_len, message_type);
394         }
395 
396         //printf("target size %i \n", ndi_ctx->target_size);
397         if(av_fifo_space(ndi_ctx->fifo) < ndi_ctx->target_size)
398             av_fifo_grow(ndi_ctx->fifo, ndi_ctx->target_size * 3 / 2);
399     }
400 
401     if(av_fifo_size(ndi_ctx->fifo) >= ndi_ctx->target_size) {
402         /* FIXME: make this zero copy */
403         uint8_t *data = malloc(ndi_ctx->target_size);
404         if(!data)
405             return -1;
406 
407         av_fifo_generic_read(ndi_ctx->fifo, data, ndi_ctx->target_size, NULL);
408         process_ndi_packet(ndi_ctx, data, ndi_ctx->target_size);
409         //printf("draining %i \n", ndi_ctx->target_size);
410         free(data);
411         ndi_ctx->target_size = 0;
412     }
413 
414     return 0;
415 }
416 
receive_ndi_packet(ndi_ctx * ndi_ctx)417 static int receive_ndi_packet(ndi_ctx *ndi_ctx)
418 {
419     uint8_t tmp[5000];
420 
421     /* TODO: Zero copy */
422     int len = recv(ndi_ctx->socket_fd, (void *)tmp, 5000, 0);
423     if(len < 0)
424         printf("bad \n");
425 
426     if(len == 0)
427         printf("end \n");
428 
429     if(av_fifo_space(ndi_ctx->fifo) < len)
430         av_fifo_grow(ndi_ctx->fifo, 5000); // fixme
431 
432     av_fifo_generic_write(ndi_ctx->fifo, tmp, len, NULL);
433 
434     if(handle_ndi_packet(ndi_ctx) < 0) {
435         printf("handle fail \n");
436         return -1;
437     }
438 
439     return 0;
440 }
441 
request_ndi_data(ndi_ctx * ndi_ctx)442 static int request_ndi_data(ndi_ctx *ndi_ctx)
443 {
444     int pending = ndi_ctx->pending_requests;
445     for(int i = 0; i < pending; i++) {
446         ndi_message *ndi_request = &ndi_ctx->ndi_request[i];
447         // XXX: Check failure
448         send(ndi_ctx->socket_fd, (void *)ndi_request->buf, ndi_request->len, 0);
449         free(ndi_request->buf);
450         ndi_request->buf = NULL;
451         ndi_ctx->pending_requests--;
452     }
453 
454     return 0;
455 }
456 
libndi_init(void)457 ndi_ctx *libndi_init(void)
458 {
459     ndi_ctx *ndi_ctx = calloc(1, sizeof(*ndi_ctx));
460     if(!ndi_ctx) {
461         fprintf(stderr, "malloc failed \n");
462         return NULL;
463     }
464 
465     ndi_ctx->fifo = av_fifo_alloc(10000);
466     if(!ndi_ctx->fifo)
467         goto end;
468 
469     return ndi_ctx;
470 
471 end:
472     libndi_close(ndi_ctx);
473     return NULL;
474 
475 }
476 
libndi_setup(ndi_ctx * ndi_ctx,ndi_opts * ndi_opts)477 int libndi_setup(ndi_ctx *ndi_ctx, ndi_opts *ndi_opts)
478 {
479     if(ndi_ctx->ip) {
480         free(ndi_ctx->ip);
481         ndi_ctx->ip = NULL;
482     }
483 
484     if(ndi_ctx->port) {
485         free(ndi_ctx->port);
486         ndi_ctx->port = NULL;
487     }
488 
489     if(!ndi_opts->ip || !ndi_opts->port) {
490         fprintf(stderr, "IP or port not set \n");
491         return -1;
492     }
493 
494     ndi_ctx->ip = strdup(ndi_opts->ip);
495     if(!ndi_ctx->ip) {
496         fprintf(stderr, "Malloc failed \n");
497         return -1;
498     }
499 
500     ndi_ctx->port = strdup(ndi_opts->port);
501     if(!ndi_ctx->port) {
502         fprintf(stderr, "Malloc failed \n");
503         return -1;
504     }
505 
506     /* Create and scramble request messages */
507     char *tx_msgs[4] = {
508         "<ndi_version text=\"3\" video=\"4\" audio=\"3\" sdk=\"3.5.1\" platform=\"LINUX\"/>",
509         "<ndi_video quality=\"high\"/>",
510         "<ndi_enabled_streams video=\"true\" audio=\"true\" text=\"true\"/>",
511     };
512 
513     char tally_msg[64];
514     int ret = snprintf(tally_msg, sizeof(tally_msg),
515         "<ndi_tally on_program=\"%s\" on_preview=\"%s\"/>",
516         (ndi_opts->initial_tally_state == NDI_TALLY_LIVE) ? "true" : "false",
517         (ndi_opts->initial_tally_state == NDI_TALLY_PREVIEW) ? "true" : "false");
518 
519     if (ret < 0 || ret >= (int)sizeof(tally_msg))
520         return -1;
521 
522     tx_msgs[3] = tally_msg;
523 
524     for(size_t i = 0; i < sizeof(tx_msgs) / sizeof(*tx_msgs); i++) {
525         size_t payload_len = strlen(tx_msgs[i]) + 1;
526         ndi_message *ndi_request = &ndi_ctx->ndi_request[i];
527         ndi_request->buf = calloc(1, payload_len + 20);
528         if(!ndi_request->buf) {
529             fprintf(stderr, "Malloc failed \n");
530             return -1;
531         }
532 
533         int dst_len = 0;
534         create_text_message(ndi_request->buf, &dst_len, tx_msgs[i], payload_len);
535         ndi_scramble_type1(ndi_request->buf+12, 8+payload_len, 8+payload_len);
536         ndi_request->len = dst_len;
537     }
538 
539     ndi_ctx->pending_requests = 4;
540 
541     return 0;
542 }
543 
544 #ifdef _WIN32
system_InitWSA(int hi,int lo)545 static int system_InitWSA(int hi, int lo) {
546     WSADATA data;
547 
548     if (WSAStartup(MAKEWORD(hi, lo), &data) == 0) {
549         if (LOBYTE(data.wVersion) == 2 && HIBYTE(data.wVersion) == 2) return 0;
550         /* Winsock DLL is not usable */
551         WSACleanup();
552     }
553     return -1;
554 }
555 
system_Init(void)556 static void system_Init(void) {
557     if (system_InitWSA(2, 2) && system_InitWSA(1, 1))
558         fputs("Error: cannot initialize Winsocks\n", stderr);
559 }
560 #else
system_Init(void)561 static void system_Init(void) { return; }
562 #endif
563 
libndi_receive_data(ndi_ctx * ndi_ctx,ndi_data_cb callback,void * user_data)564 void libndi_receive_data(ndi_ctx *ndi_ctx, ndi_data_cb callback, void *user_data)
565 {
566     ndi_ctx->callback = callback;
567     ndi_ctx->user_data = user_data;
568 
569     /* connect to socket */
570     int ret;
571     struct addrinfo hints, *res, *p;
572     memset(&hints, 0, sizeof hints);
573     hints.ai_family = AF_UNSPEC;
574     hints.ai_socktype = SOCK_STREAM;
575 
576     system_Init();
577 
578     if ((ret = getaddrinfo(ndi_ctx->ip, ndi_ctx->port, &hints, &res)) != 0) {
579         fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret));
580         goto end;
581     }
582 
583     for(p = res; p != NULL; p = p->ai_next) {
584         ndi_ctx->socket_fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
585         if(ndi_ctx->socket_fd < 0)
586             continue;
587 
588         ret = connect(ndi_ctx->socket_fd, res->ai_addr, res->ai_addrlen);
589         if(ret < 0) {
590             printf("can't connect \n");
591             freeaddrinfo(res);
592             goto end;
593         }
594     }
595 
596     freeaddrinfo(res);
597 
598     struct pollfd fds[1];
599     fds[0].fd = ndi_ctx->socket_fd;
600     fds[0].events = POLLIN | POLLOUT;
601     while(poll(fds, 1, 10000)) {
602         if(fds[0].revents & POLLOUT) {
603             if(request_ndi_data(ndi_ctx) < 0)
604                 goto end;
605 
606             if(!ndi_ctx->pending_requests)
607                 fds[0].events = POLLIN;
608         }
609 
610         if(fds[0].revents & POLLIN)
611             receive_ndi_packet(ndi_ctx);
612     }
613 
614 end:
615     return;
616 }
617 
libndi_close(ndi_ctx * ndi_ctx)618 void libndi_close(ndi_ctx *ndi_ctx)
619 {
620     if(!ndi_ctx)
621         return;
622 
623     if(ndi_ctx->ip)
624         free(ndi_ctx->ip);
625 
626     if(ndi_ctx->port)
627         free(ndi_ctx->port);
628 
629     for(int i = 0; i < 4; i++)
630         free(ndi_ctx->ndi_request[i].buf);
631 
632     av_fifo_free(ndi_ctx->fifo);
633 
634     free(ndi_ctx);
635 }
636