UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
import.c
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2012-2023 CESNET, z. s. p. o.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
47#ifdef HAVE_CONFIG_H
48#include "config.h"
49#include "config_unix.h"
50#include "config_win32.h"
51#endif // HAVE_CONFIG_H
52
53#include "audio/types.h"
54#include "audio/wav_reader.h"
55#include "debug.h"
56#include "host.h"
57#include "lib_common.h"
58#include "keyboard_control.h"
59#include "messaging.h"
60#include "module.h"
61#include "playback.h"
62#include "tv.h"
63#include "utils/color_out.h"
64#include "utils/fs.h"
65#include "utils/macros.h"
66#include "utils/ring_buffer.h"
67#include "utils/worker.h"
68#include "video.h"
69#include "video_capture.h"
70#include "video_export.h"
71
72#include <pthread.h>
73#include <stdio.h>
74#include <stdlib.h>
75#include <fcntl.h>
76#include <stdbool.h>
77#include <string.h>
78#include <sys/stat.h>
79#include <sys/time.h>
80#include <sys/types.h>
81#include <unistd.h>
82
83#define BUFFER_LEN_MAX 40
84#define MAX_CLIENTS 16
85
86#define VIDCAP_IMPORT_ID 0x76FA7F6D
87
88#define PIPE "/tmp/ultragrid_import.fifo"
89
90#define MAX_NUMBER_WORKERS 100
91#define MOD_NAME "[import] "
92
93struct processed_entry;
94struct tile_data {
95 char *data;
97};
98
104
110
111struct import_message;
112
119
124
129
133 size_t len;
134};
135
138 FILE *file;
143 pthread_t thread_id;
144
145 pthread_cond_t worker_cv;
146 pthread_cond_t boss_cv;
147
148 pthread_mutex_t lock;
149 long long int played_samples;
151
153};
154
156 struct module mod;
157 struct module *parent;
162 char tile_delim; // eg. '_' for format "00000001_0.yuv"
163
165
166 pthread_mutex_t lock;
167 pthread_cond_t worker_cv;
168 pthread_cond_t boss_cv;
169 struct processed_entry * volatile head, * volatile tail;
170 volatile int queue_len;
171
173
174 struct timeval prev_time;
176
179 bool loop;
183 double force_fps;
184};
185
186static void * audio_reading_thread(void *args);
187static void * video_reading_thread(void *args);
188static void import_send_message(struct import_message *msg, struct message_queue *queue);
189static struct import_message* pop_message(struct message_queue *queue);
190static int flush_processed(struct processed_entry *list);
191static void message_queue_clear(struct message_queue *queue);
192static void vidcap_import_new_message(struct module *);
193static void process_msg(struct vidcap_import_state *state, const char *message);
194
195static void cleanup_common(struct vidcap_import_state *s);
196
197static void message_queue_clear(struct message_queue *queue) {
198 queue->head = queue->tail = NULL;
199 queue->len = 0;
200}
201
202static void vidcap_import_probe(struct device_info **available_cards, int *count, void (**deleter)(void *))
203{
204 *deleter = free;
205 *available_cards = NULL;
206 *count = 0;
207}
208
209#define READ_N(buf, len) if (fread(buf, len, 1, audio_file) != 1) goto error_format;
210
211static bool init_audio(struct vidcap_import_state *s, const char *audio_filename)
212{
213 FILE *audio_file = fopen(audio_filename, "rb");
214 if(!audio_file) {
215 perror("Cannot open audio file");
216 return false;
217 }
218
219 int ret = read_wav_header(audio_file, &s->audio_state.metadata);
220 if (ret != WAV_HDR_PARSE_OK) {
221 log_msg(LOG_LEVEL_ERROR, "%s!\n", get_wav_error(ret));
222 goto error_format;
223 }
224
230
232 s->audio_frame.ch_count * 180);
233
235 s->audio_frame.data_len = 0;
236 s->audio_frame.data = (char *) malloc(s->audio_frame.max_size);
237
238 s->audio_state.file = audio_file;
239
240 pthread_cond_init(&s->audio_state.worker_cv, NULL);
241 pthread_cond_init(&s->audio_state.boss_cv, NULL);
242 pthread_mutex_init(&s->audio_state.lock, NULL);
243
244 return true;
245
246error_format:
247 fprintf(stderr, "Audio format file error - unknown format\n");
248 fclose(audio_file);
249 return false;
250}
251
253static long strtol_checked(const char *line, const char *prefix, long min_val, long max_val) {
254 long int val = strtol(line + strlen(prefix), NULL, 10);
255 if (val == LONG_MIN || val == LONG_MAX) {
256 log_msg(LOG_LEVEL_ERROR, MOD_NAME "cannot read %sline.\n", prefix);
257 return LONG_MIN;
258 }
259 if (val < min_val || val > max_val) {
260 log_msg(LOG_LEVEL_ERROR, MOD_NAME "%sout of range [%ld..%ld]\n", prefix, min_val, max_val);
261 return LONG_MIN;
262 }
263 return val;
264}
265
266static struct video_desc parse_video_desc_info(FILE *info, long *video_frame_count) {
267 struct video_desc desc = { 0 };
268
269 char line[512];
270 uint32_t items_found = 0;
271 while (fgets(line, sizeof(line), info) != NULL) {
272 long val = 0;
273 if(strncmp(line, "version ", strlen("version ")) == 0) {
275 return (struct video_desc) { 0 };
276 }
277 items_found |= 1U<<0U;
278 } else if(strncmp(line, "width ", strlen("width ")) == 0) {
279 if ((val = strtol_checked(line, "width ", 0, INT_MAX)) == LONG_MIN) {
280 return (struct video_desc) { 0 };
281 }
282 desc.width = val;
283 items_found |= 1U<<1U;
284 } else if(strncmp(line, "height ", strlen("height ")) == 0) {
285 if ((val = strtol_checked(line, "height ", 0, INT_MAX)) == LONG_MIN) {
286 return (struct video_desc) { 0 };
287 }
288 desc.height = val;
289 items_found |= 1U<<2U;
290 } else if(strncmp(line, "fourcc ", strlen("fourcc ")) == 0) {
291 char *ptr = line + strlen("fourcc ");
292 if(strlen(ptr) != 5) { // including '\n'
293 log_msg(LOG_LEVEL_ERROR, MOD_NAME "cannot read video FourCC tag.\n");
294 return (struct video_desc) { 0 };
295 }
296 uint32_t fourcc = 0U;
297 memcpy((void *) &fourcc, ptr, sizeof(fourcc));
300 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Requested codec not known.\n");
301 return (struct video_desc) { 0 };
302 }
303 items_found |= 1U<<3U;
304 } else if(strncmp(line, "fps ", strlen("fps ")) == 0) {
305 char *ptr = line + strlen("fps ");
306 desc.fps = strtod(ptr, NULL);
307 if(desc.fps == HUGE_VAL || desc.fps <= 0) {
308 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Invalid FPS.\n");
309 return (struct video_desc) { 0 };
310 }
311 items_found |= 1U<<4U;
312 } else if(strncmp(line, "interlacing ", strlen("interlacing ")) == 0) {
313 if ((val = strtol_checked(line, "interlacing ", 0, INTERLACING_MAX)) == LONG_MIN) {
314 return (struct video_desc) { 0 };
315 }
317 items_found |= 1U<<5U;
318 } else if(strncmp(line, "count ", strlen("count ")) == 0) {
319 if ((val = strtol_checked(line, "count ", 0, LONG_MAX)) == LONG_MIN) {
320 return (struct video_desc) { 0 };
321 };
322 *video_frame_count = val;
323 items_found |= 1U<<6U;
324 }
325 }
326
327 if(items_found != (1U << 7U) - 1U) {
328 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Failed while reading config file - some items missing.\n");
329 return (struct video_desc) { 0 };
330 }
331
332 assert((desc.color_spec != VIDEO_CODEC_NONE && desc.width != 0 && desc.height != 0 && desc.fps != 0.0 &&
333 *video_frame_count != 0));
334 return desc;
335}
336
337static int get_tile_count(const char *directory, codec_t color_spec, char *tile_delim) {
338 char name[1024];
339 snprintf(name, sizeof(name), "%s/%08d.%s", directory, 1,
341
342 struct stat sb;
343 if (stat(name, &sb) == 0) {
344 return 1;
345 }
346 int tile_count = 0;
347 char possible_tile_delim[] = { '_', '-' };
348 for (unsigned int d = 0; d < sizeof possible_tile_delim; d++) {
349 for (int i = 0; i < 10; i++) {
350 snprintf(name, sizeof(name), "%s/%08d%c%d.%s",
351 directory, 1,
352 possible_tile_delim[d], i,
353 get_codec_file_extension(color_spec));
354 if (stat(name, &sb) == 0) {
355 tile_count++;
356 } else {
357 break;
358 }
359 }
360 if (tile_count > 0) {
361 *tile_delim = possible_tile_delim[d];
362 break;
363 }
364 }
365 if (tile_count == 0) {
366 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to open first file of "
367 "the video sequence.");
368 return 0;
369 }
370 return tile_count;
371}
372
373static bool initialize_import(struct vidcap_import_state *s, char *tmp, FILE **info, unsigned int flags) {
374 bool disable_audio = false;
375
378 s->mod.priv_data = s;
380 module_register(&s->mod, s->parent);
381
382 s->video_reading_threads_count = 1; // default is single threaded
383
384 char *save_ptr = NULL;
385 char *suffix;
386 s->directory = strtok_r(tmp, ":", &save_ptr);
387 if (s->directory == NULL) {
388 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Wrong directory name!\n");
389 return false;
390 }
391 s->directory = strdup(s->directory); // make a copy
392
393 while ((suffix = strtok_r(NULL, ":", &save_ptr)) != NULL) {
394 if (suffix[0] == '\\') { // MSW path
395 assert(strlen(s->directory) == 1); // c:\something -> should be 'c'
396 const size_t len = 2 + strlen(suffix) + 1;
397 char *tmp = (char *) malloc(len);
398 snprintf(tmp, len, "%c:%s", s->directory[0], suffix);
399 free(s->directory);
400 s->directory = tmp;
401 } else if (strcmp(suffix, "loop") == 0) {
402 s->loop = true;
403 } else if (strncmp(suffix, "mt_reading=",
404 strlen("mt_reading=")) == 0) {
406 strlen("mt_reading="));
407 assert(s->video_reading_threads_count <=
409 } else if (strcmp(suffix, "o_direct") == 0) {
410 s->o_direct = true;
411 } else if (strcmp(suffix, "noaudio") == 0) {
412 disable_audio = true;
413 } else if (strcmp(suffix, "opportunistic_audio") == 0) { // skip
414 } else if (strcmp(suffix, "exit_at_end") == 0) {
415 s->should_exit_at_end = true;
416 } else if (strncmp(suffix, "fps=", strlen("fps=")) == 0) {
417 s->force_fps = atof(suffix + strlen("fps="));
418 } else if (strstr(suffix, "frames=") == suffix) {
419 s->video_frame_count = strtol(strchr(suffix, '=') + 1, NULL, 10);
420 } else {
421 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unrecognized option: %s.\n",
422 suffix);
423 return false;
424 }
425 }
426
427 // strip video.info if user included in path
428 if (strstr(s->directory, "video.info") == s->directory) {
429 strcpy(s->directory, ".");
430 }
431 if (strstr(s->directory, "/video.info") != NULL) {
432 *strrchr(s->directory, '/') = '\0';
433 }
434
437
438 char filename[MAX_PATH_SIZE] = "";
439 strncpy(filename, s->directory, sizeof filename - 1);
440 strncat(filename, "/sound.wav", sizeof filename - strlen(filename) - 1);
441 if((flags & VIDCAP_FLAG_AUDIO_EMBEDDED) && !disable_audio && init_audio(s, filename)) {
442 s->audio_state.has_audio = true;
443 }
444
445 strncpy(filename, s->directory, sizeof filename - 1);
446 strncat(filename, "/video.info", sizeof filename - strlen(filename) - 1);
447 *info = fopen(filename, "r");
448 if (*info == NULL) {
449 perror(MOD_NAME "Failed to open video index file");
450 if (!s->audio_state.has_audio) {
451 if (errno == ENOENT) {
452 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Invalid directory?\n");
453 }
454 return false;
455 }
456 s->has_video = false;
457 s->video_desc.fps = 30; // used to sample audio
458 }
459
460 if (s->has_video) {
461 long frame_count = 0;
462 s->video_desc = parse_video_desc_info(*info, &frame_count);
463 if (s->video_desc.width == 0) {
464 return false;
465 }
466 s->video_frame_count = s->video_frame_count == 0 ? frame_count : MIN(s->video_frame_count, frame_count);
467
469 if (s->video_desc.tile_count == 0) {
470 return false;
471 }
472 }
473
474 // override metadata fps setting
475 if (s->force_fps > 0.0) {
476 s->video_desc.fps = s->force_fps;
477 }
478
479 if (s->audio_state.has_audio) {
480 if(pthread_create(&s->audio_state.thread_id, NULL, audio_reading_thread, (void *) s) != 0) {
481 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to create thread.\n");
482 return false;
483 }
484 }
485 if (s->has_video) {
486 if (pthread_create(&s->video_thread_id, NULL, video_reading_thread, (void *) s) != 0) {
487 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to create thread.\n");
488 return false;
489 }
490 }
491
492 gettimeofday(&s->prev_time, NULL);
493
495
496 return true;
497}
498
499static int
501{
502 char *tmp = strdup(vidcap_params_get_fmt(params));
503 if (strlen(tmp) == 0 || strcmp(tmp, "help") == 0) {
504 color_printf("Import usage:\n"
505 TERM_BOLD TERM_FG_RED "\t<directory>" TERM_FG_RESET "{:loop|:mt_reading=<nr_threads>|:o_direct|:exit_at_end|:fps=<fps>|frames=<n>|:disable_audio}\n" TERM_RESET
506 "where\n"
507 TERM_BOLD "\t<fps>" TERM_RESET " - overrides FPS from sequence metadata\n"
508 TERM_BOLD "\t<n> " TERM_RESET " - use only N first frames fron sequence (if less than available frames)\n");
509 free(tmp);
510 return VIDCAP_INIT_NOERR;
511 }
512
513 FILE *info = NULL; // metadata file
514 struct vidcap_import_state *s = (struct vidcap_import_state *) calloc(1, sizeof *s);
515 pthread_cond_init(&s->worker_cv, NULL);
516 pthread_cond_init(&s->boss_cv, NULL);
517 pthread_mutex_init(&s->lock, NULL);
519 s->has_video = true;
520
521 printf("vidcap_import_init\n");
522 bool init_success = initialize_import(s, tmp, &info, vidcap_params_get_flags(params));
523 free(tmp);
524 if (info != NULL) {
525 fclose(info);
526 }
527 if (init_success) {
528 *state = s;
529 return VIDCAP_INIT_OK;
530 }
532 return VIDCAP_INIT_FAIL;
533}
534
536{
537 if (s->has_video) {
538 struct import_message *msg = (struct import_message *) malloc(sizeof(struct import_message));
539
540 msg->type = FINALIZE;
541 msg->data = NULL;
542 msg->data_len = 0;
543 msg->next = NULL;
544
545 pthread_mutex_lock(&s->lock);
547 pthread_mutex_unlock(&s->lock);
548 pthread_cond_signal(&s->worker_cv);
549
550 pthread_join(s->video_thread_id, NULL);
551 }
552
553 // audio
554 if(s->audio_state.has_audio) {
555 struct import_message *msg = (struct import_message *) malloc(sizeof(struct import_message));
556
557 msg->type = FINALIZE;
558 msg->data = NULL;
559 msg->data_len = 0;
560 msg->next = NULL;
561
562 {
563 pthread_mutex_lock(&s->audio_state.lock);
565 pthread_mutex_unlock(&s->audio_state.lock);
566 pthread_cond_signal(&s->audio_state.worker_cv);
567 }
568
569 pthread_join(s->audio_state.thread_id, NULL);
570 }
571}
572
573static void free_entry(struct processed_entry *entry)
574{
575 if (entry == NULL) {
576 return;
577 }
578 for (int i = 0; i < entry->count; ++i) {
579 aligned_free(entry->tiles[i].data);
580 }
581
582 free(entry);
583}
584
585static void vidcap_import_finish(void *state)
586{
587 struct vidcap_import_state *s = (struct vidcap_import_state *) state;
588
590}
591
592static int flush_processed(struct processed_entry *list)
593{
594 int frames_deleted = 0;
595 struct processed_entry *current = list;
596
597 while(current != NULL) {
598 struct processed_entry *tmp = current;
599 current = current->next;
600 free_entry(tmp);
601 frames_deleted++;
602 }
603
604 return frames_deleted;
605}
606
607static void cleanup_common(struct vidcap_import_state *s) {
609
610 free(s->directory);
611
612 // audio
613 if(s->audio_state.has_audio) {
615
616 free(s->audio_frame.data);
617
618 fclose(s->audio_state.file);
619 pthread_cond_destroy(&s->audio_state.worker_cv);
620 pthread_cond_destroy(&s->audio_state.boss_cv);
621 pthread_mutex_destroy(&s->audio_state.lock);
622 }
623
624 module_done(&s->mod);
625 pthread_cond_destroy(&s->worker_cv);
626 pthread_cond_destroy(&s->boss_cv);
627 pthread_mutex_destroy(&s->lock);
628 free(s);
629}
630
631static void vidcap_import_done(void *state)
632{
633 struct vidcap_import_state *s = (struct vidcap_import_state *) state;
634 assert(s != NULL);
635
637
639}
640
641static void import_send_message(struct import_message *msg, struct message_queue *queue)
642{
643 if(queue->head) {
644 queue->tail->next = msg;
645 queue->tail = msg;
646 } else {
647 queue->head = queue->tail = msg;
648 }
649
650 queue->len += 1;
651}
652
653static struct import_message *pop_message(struct message_queue *queue)
654{
655 assert(queue->len > 0);
656 struct import_message *ret;
657
658 ret = queue->head;
659 queue->head = queue->head->next;
660 if(queue->head == NULL) {
661 queue->tail = NULL;
662 }
663
664 queue->len -= 1;
665
666 return ret;
667}
668
669static void vidcap_import_new_message(struct module *mod) {
670 struct msg_universal *m;
671 while ((m = (struct msg_universal *) check_message(mod))) {
672 process_msg((struct vidcap_import_state *) mod->priv_data, m->text);
673 free_message((struct message *) m, new_response(RESPONSE_ACCEPTED, "import is processing the request"));
674 }
675}
676
677static void process_msg(struct vidcap_import_state *s, const char *message)
678{
679 if(strcasecmp(message, "pause") == 0) {
680 struct import_message *msg = (struct import_message *) malloc(sizeof(struct import_message));
681 msg->type = PAUSE;
682 msg->data = NULL;
683 msg->data_len = 0;
684 msg->next = NULL;
685
686 pthread_mutex_lock(&s->lock);
688 pthread_mutex_unlock(&s->lock);
689 pthread_cond_signal(&s->worker_cv);
690 } else if(strncasecmp(message, "seek ", strlen("seek ")) == 0) {
691 const char *time_spec = message + strlen("seek ");
692
693 struct import_message *msg = (struct import_message *) malloc(sizeof(struct import_message));
694 struct seek_data *data = (struct seek_data *) malloc(sizeof(struct seek_data));
695 msg->type = SEEK;
696 msg->data = data;
697 msg->data_len = sizeof(struct seek_data);
698 msg->next = NULL;
699
700 if(time_spec[0] == '+' || time_spec[0] == '-') {
701 data->whence = IMPORT_SEEK_CUR;
702 if(strchr(time_spec, 's') != NULL) {
703 double val = atof(time_spec);
704 data->offset = val * s->video_desc.fps;
705 } else {
706 data->offset = atoi(time_spec);
707 }
708 } else {
709 data->whence = IMPORT_SEEK_SET;
710 if(strchr(time_spec, 's') != NULL) {
711 double val = atof(time_spec);
712 data->offset = val * s->video_desc.fps;
713 } else {
714 data->offset = atoi(time_spec);
715 }
716 }
717
718 struct import_message *audio_msg = NULL;
719
720 if(s->audio_state.has_audio) {
721 audio_msg = (struct import_message *) malloc(sizeof(struct import_message));
722 memcpy(audio_msg, msg, sizeof(struct import_message));
723
724 if(audio_msg->data) { // deep copy
725 audio_msg->data = malloc(msg->data_len);
726 memcpy(audio_msg->data, msg->data, msg->data_len);
727 }
728 }
729
730 {
731 pthread_mutex_lock(&s->lock);
733 pthread_mutex_unlock(&s->lock);
734 pthread_cond_signal(&s->worker_cv);
735 }
736
737 if (s->audio_state.has_audio) {
738 pthread_mutex_lock(&s->audio_state.lock);
740 pthread_mutex_unlock(&s->audio_state.lock);
741 pthread_cond_signal(&s->audio_state.worker_cv);
742 }
743 } else if(strcasecmp(message, "quit") == 0) {
744 exit_uv(0);
745 } else {
746 fprintf(stderr, "Warning: unknown message: \'%s\'\n", message);
747 }
748}
749
750struct client;
751
752struct client {
753 int fd;
754 char buff[1024];
755 int buff_len;
756 bool pipe;
757
758 struct client *next;
759};
760
761static void * audio_reading_thread(void *args)
762{
763 struct vidcap_import_state *s = (struct vidcap_import_state *) args;
764
765 //while(s->audio_state.samples_read < s->audio_state.total_samples && !s->finish_threads) {
766 while(1) {
767 int max_read;
768 {
769 pthread_mutex_lock(&s->audio_state.lock);
772 && s->audio_state.message_queue.len == 0) {
773 pthread_cond_wait(&s->audio_state.worker_cv, &s->audio_state.lock);
774 }
775
776 while(s->audio_state.message_queue.len > 0) {
778 if(msg->type == FINALIZE) {
779 free(msg);
780 pthread_mutex_unlock(&s->audio_state.lock);
781 return NULL;
782 } else if (msg->type == SEEK) {
783 struct seek_data *data = (struct seek_data *) msg->data;
784 free(msg);
785 long long bytes = (s->audio_frame.sample_rate * data->offset / s->video_desc.fps) * s->audio_frame.bps * s->audio_frame.ch_count;
786 if (data->whence == IMPORT_SEEK_CUR) {
788 bytes = MAX(0, bytes);
789 }
790 int ret = wav_seek(s->audio_state.file, bytes, SEEK_SET, &s->audio_state.metadata);
791 log_msg(LOG_LEVEL_NOTICE, "Audio seek %lld bytes\n", bytes);
792 if (ret != 0) {
793 perror("wav_seek");
794 }
800 free(data);
801 }
802 }
803
804 max_read = ring_get_size(s->audio_state.data) -
806
807 pthread_mutex_unlock(&s->audio_state.lock);
808 }
809
810 char *buffer = (char *) malloc(max_read);
811
812 size_t samples = wav_read(buffer, max_read / s->audio_frame.ch_count / s->audio_frame.bps, s->audio_state.file, &s->audio_state.metadata);
813 s->audio_state.samples_read += samples;
814
815 {
816 pthread_mutex_lock(&s->audio_state.lock);
818 pthread_mutex_unlock(&s->audio_state.lock);
819 pthread_cond_signal(&s->audio_state.boss_cv);
820 }
821
822 free(buffer);
823 }
824
825 return NULL;
826}
827
836
837#define ALLOC_ALIGN 512
838
839static void *video_reader_callback(void *arg)
840{
841 struct video_reader_data *data =
842 (struct video_reader_data *) arg;
843
844 data->entry = (struct processed_entry *) calloc(1, sizeof(struct processed_entry) + data->tile_count * sizeof(struct tile_data));
845 assert(data->entry != NULL);
846 data->entry->next = NULL;
847 data->entry->count = data->tile_count;
848
849 for (unsigned int i = 0; i < data->tile_count; i++) {
850 char name[1048];
851 char tile_idx[3] = "";
852 if (data->tile_count > 1) {
853 snprintf(tile_idx, sizeof tile_idx, "%c%d", data->tile_delim, i);
854 }
855 snprintf(name, sizeof(name), "%s%s.%s",
856 data->file_name_prefix, tile_idx,
857 data->file_name_suffix);
858
859 struct stat sb;
860
861 int flags = O_RDONLY;
862#ifdef _WIN32
863 flags |= O_BINARY;
864#endif
865 if (data->o_direct) {
866#ifdef __linux__
867 flags |= O_DIRECT;
868#endif
869 }
870 int fd = open(name, flags);
871 if(fd == -1) {
872 perror("open");
873 return NULL;
874 }
875 if (fstat(fd, &sb)) {
876 perror("fstat");
877 close(fd);
878 free_entry(data->entry);
879 return NULL;
880 }
881
882 data->entry->tiles[i].data_len = sb.st_size;
883 const int aligned_data_len = (data->entry->tiles[i].data_len + ALLOC_ALIGN - 1)
885 // alignment needed when using O_DIRECT flag
886 data->entry->tiles[i].data = (char *)
887 aligned_malloc(aligned_data_len, ALLOC_ALIGN);
888 assert(data->entry->tiles[i].data != NULL);
889
890 ssize_t bytes = 0;
891 do {
892 ssize_t res = read(fd, data->entry->tiles[i].data + bytes,
893 (data->entry->tiles[i].data_len - bytes + ALLOC_ALIGN - 1)
895 if (res <= 0) {
896 perror("read");
897 for (unsigned int i = 0; i < data->tile_count; i++) {
898 aligned_free(data->entry->tiles[i].data);
899 }
900 free(data->entry);
901 close(fd);
902 return NULL;
903 }
904 bytes += res;
905 } while (bytes < data->entry->tiles[i].data_len);
906
907 close(fd);
908 }
909
910 return data;
911}
912
913static void * video_reading_thread(void *args)
914{
915 struct vidcap_import_state *s = (struct vidcap_import_state *) args;
916 long index = 0;
917
918 bool paused = false;
919
921 while(1) {
922 {
923 pthread_mutex_lock(&s->lock);
924 while((s->queue_len >= BUFFER_LEN_MAX - 1 || index >= s->video_frame_count || paused)
925 && s->message_queue.len == 0) {
926 if (index >= s->video_frame_count) {
927 s->finished = true;
928 }
929 pthread_cond_wait(&s->worker_cv, &s->lock);
930 }
931
932 while(s->message_queue.len > 0) {
934 if(msg->type == FINALIZE) {
935 free(msg);
936 pthread_mutex_unlock(&s->lock);
937 return NULL;
938 } else if(msg->type == PAUSE) {
939 paused = !paused;
940 printf("Toggle pause\n");
941
942 index -= flush_processed(s->head);
943 s->queue_len = 0;
944 s->head = s->tail = NULL;
945
946 free(msg);
947 } else if (msg->type == SEEK) {
949 s->queue_len = 0;
950 s->head = s->tail = NULL;
951
952 struct seek_data *data = (struct seek_data *) msg->data;
953 free(msg);
954 if(data->whence == IMPORT_SEEK_CUR) {
955 index += data->offset;
956 } else if (data->whence == IMPORT_SEEK_SET) {
957 index = data->offset;
958 }
959 index = MIN(MAX(0L, index), s->video_frame_count - 1);
960 printf("Current index: frame %ld\n", index);
961 free(data);
962 } else {
963 fprintf(stderr, "Unknown message type: %d!\n", msg->type);
964 abort();
965 }
966 }
967 pthread_mutex_unlock(&s->lock);
968 }
969
971 index = MIN(MAX(0L, index), s->video_frame_count - 1);
972
973 struct video_reader_data data_reader[MAX_NUMBER_WORKERS];
975
976 int number_workers = s->video_reading_threads_count;
977 if (index + number_workers >= s->video_frame_count) {
978 number_workers = s->video_frame_count - index;
979 }
980 // run workers
981 for (int i = 0; i < number_workers; ++i) {
982 struct video_reader_data *data =
983 &data_reader[i];
984 data->o_direct = s->o_direct;
986 data->tile_delim = s->tile_delim;
987 snprintf(data->file_name_prefix, sizeof(data->file_name_prefix),
988 "%s/%08ld", s->directory, index + i + 1);
989 strncpy(data->file_name_suffix,
991 sizeof(data->file_name_suffix));
992 data->entry = NULL;
993 task_handle[i] = task_run_async(video_reader_callback, data);
994 }
995
996 // wait for workers to finish
997 for (int i = 0; i < number_workers; ++i) {
998 struct video_reader_data *data =
999 (struct video_reader_data *)
1000 wait_task(task_handle[i]);
1001 if (!data || data->entry == NULL)
1002 continue;
1003 {
1004 pthread_mutex_lock(&s->lock);
1005 if(s->head) {
1006 s->tail->next = data->entry;
1007 s->tail = data->entry;
1008 } else {
1009 s->head = s->tail = data->entry;
1010 }
1011 s->queue_len += 1;
1012
1013 pthread_mutex_unlock(&s->lock);
1014 pthread_cond_signal(&s->boss_cv);
1015 }
1016 }
1017 index += number_workers;
1018 }
1019
1020 return NULL;
1021}
1022
1023static void reset_import(struct vidcap_import_state *s)
1024{
1026
1027 s->finished = false;
1028
1029 // clear audio state
1032 if (s->audio_state.has_audio) {
1036 if (wav_seek(s->audio_state.file, 0L, SEEK_SET, &s->audio_state.metadata) != 0) {
1037 perror("wav_seek");
1038 }
1040 }
1041
1042 if (s->has_video) {
1043 if (pthread_create(&s->video_thread_id, NULL, video_reading_thread, (void *) s) != 0) {
1044 fprintf(stderr, "Unable to create thread.\n");
1046 abort();
1047 }
1048 }
1049 if (s->audio_state.has_audio) {
1050 if(pthread_create(&s->audio_state.thread_id, NULL, audio_reading_thread, (void *) s) != 0) {
1051 fprintf(stderr, "Unable to create thread.\n");
1052 abort();
1053 }
1054 }
1055}
1056
1061
1062static unsigned long long get_req_bytes(struct vidcap_import_state *s) {
1063 long long int requested_samples = (long long int) (s->audio_state.video_frames_played + 0) *
1065 if (requested_samples <= 0) {
1066 return 0LL;
1067 }
1068 if ((s->audio_state.played_samples + requested_samples) > s->audio_state.total_samples) {
1069 requested_samples = s->audio_state.total_samples - s->audio_state.played_samples;
1070 }
1071 return requested_samples * s->audio_frame.bps * s->audio_frame.ch_count;
1072}
1073
1074static struct video_frame *
1076{
1077 struct vidcap_import_state *s = (struct vidcap_import_state *) state;
1078 struct timeval cur_time;
1079 struct video_frame *ret = NULL;
1080
1081 struct processed_entry *current = NULL;
1082
1083 if (s->has_video) {
1084 pthread_mutex_lock(&s->lock);
1085 if(s->queue_len == 0) {
1086 if (s->finished == true && s->loop) {
1087 pthread_mutex_unlock(&s->lock);
1088 log_msg(LOG_LEVEL_NOTICE, MOD_NAME "Rewinding the sequence.\n");
1089 reset_import(s);
1090 pthread_mutex_lock(&s->lock);
1091 }
1092 if (s->finished == true && s->should_exit_at_end == true) {
1093 exit_uv(0);
1094 }
1095 struct timespec ts;
1098 pthread_cond_timedwait(&s->boss_cv, &s->lock, &ts);
1099 }
1100
1101 if(s->queue_len == 0) {
1102 pthread_mutex_unlock(&s->lock);
1103 return NULL;
1104 }
1105
1106 current = s->head;
1107 assert(current != NULL);
1108
1109 s->head = s->head->next;
1110 s->queue_len -= 1;
1111
1112 pthread_mutex_unlock(&s->lock);
1113 pthread_cond_signal(&s->worker_cv);
1114
1115 ret = vf_alloc_desc(s->video_desc);
1117 ret->callbacks.dispose_udata = current;
1118 for (unsigned int i = 0; i < s->video_desc.tile_count; ++i) {
1119 ret->tiles[i].data_len =
1120 current->tiles[i].data_len;
1121 ret->tiles[i].data = current->tiles[i].data;
1122 }
1123 }
1124
1125 // audio
1126 if(s->audio_state.has_audio) {
1127 pthread_mutex_lock(&s->audio_state.lock);
1128 unsigned long long int requested_bytes = MIN(get_req_bytes(s), (unsigned long long) s->audio_frame.max_size);
1129 while(ring_get_current_size(s->audio_state.data) < (int) requested_bytes) {
1130 pthread_cond_wait(&s->audio_state.boss_cv, &s->audio_state.lock);
1131 requested_bytes = MIN(get_req_bytes(s), (unsigned long long) s->audio_frame.max_size);
1132 }
1133
1134 int ret = ring_buffer_read(s->audio_state.data, s->audio_frame.data, requested_bytes);
1135 s->audio_frame.data_len = ret;
1137 *audio = &s->audio_frame;
1139 pthread_mutex_unlock(&s->audio_state.lock);
1140 pthread_cond_signal(&s->audio_state.worker_cv);
1141 } else {
1142 *audio = NULL;
1143 }
1144
1145 gettimeofday(&cur_time, NULL);
1146 while(tv_diff_usec(cur_time, s->prev_time) < 1000000.0 / s->video_desc.fps) {
1147 gettimeofday(&cur_time, NULL);
1148 }
1149 //tv_add_usec(&s->prev_time, 1000000.0 / s->frame->fps);
1150 s->prev_time = cur_time;
1151
1152 return ret;
1153}
1154
1162
1164
void ** state
int void * val
struct video_desc desc
struct video_frame * frame
int void size_t * len
const char unsigned int flags
struct audio_frame ** audio
int void(** deleter)(void *))
void vf_free(struct video_frame *buf)
Frees video_frame structure.
struct video_frame * vf_alloc_desc(struct video_desc desc)
Allocates video frame accordig given desc.
This file contains definition of some common audio types.
int color_printf(const char *format,...)
#define TERM_BOLD
Definition color_out.h:48
#define TERM_FG_RED
Definition color_out.h:50
#define TERM_RESET
Definition color_out.h:47
#define TERM_FG_RESET
Definition color_out.h:59
#define suffix(x, y)
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_NOTICE
information that may be interesting
Definition debug.h:61
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define MAX_PATH_SIZE
Definition fs.h:48
void module_init_default(struct module *module_data)
Definition module.c:48
void module_register(struct module *module_data, struct module *parent)
Definition module.c:82
void module_done(struct module *module_data)
Definition module.c:93
@ MODULE_CLASS_DATA
Definition module.h:86
#define VIDCAP_INIT_NOERR
state not initialized, other action performed (typically help)
#define VIDCAP_INIT_OK
initialization successful
#define VIDEO_CAPTURE_ABI_VERSION
#define VIDCAP_INIT_FAIL
error ocured
int count
Definition hd-rum.c:44
void exit_uv(int status)
Definition host.cpp:625
static long strtol_checked(const char *line, const char *prefix, long min_val, long max_val)
Definition import.c:253
static void * video_reading_thread(void *args)
Definition import.c:913
static void import_send_message(struct import_message *msg, struct message_queue *queue)
Definition import.c:641
static void * video_reader_callback(void *arg)
Definition import.c:839
static struct video_desc parse_video_desc_info(FILE *info, long *video_frame_count)
Definition import.c:266
static void cleanup_common(struct vidcap_import_state *s)
Definition import.c:607
static const struct video_capture_info vidcap_import_info
Definition import.c:1155
static void process_msg(struct vidcap_import_state *state, const char *message)
Definition import.c:677
static void vidcap_import_dispose_video_frame(struct video_frame *frame)
Definition import.c:1057
static bool initialize_import(struct vidcap_import_state *s, char *tmp, FILE **info, unsigned int flags)
Definition import.c:373
import_message_t
Definition import.c:105
@ FINALIZE
Definition import.c:107
@ PAUSE
Definition import.c:108
@ SEEK
Definition import.c:106
static int flush_processed(struct processed_entry *list)
Definition import.c:592
static void free_entry(struct processed_entry *entry)
Definition import.c:573
static struct video_frame * vidcap_import_grab(void *state, struct audio_frame **audio)
Definition import.c:1075
static void message_queue_clear(struct message_queue *queue)
Definition import.c:197
static void exit_reading_threads(struct vidcap_import_state *s)
Definition import.c:535
static void * audio_reading_thread(void *args)
Definition import.c:761
static struct import_message * pop_message(struct message_queue *queue)
Definition import.c:653
static int vidcap_import_init(struct vidcap_params *params, void **state)
Definition import.c:500
static unsigned long long get_req_bytes(struct vidcap_import_state *s)
Definition import.c:1062
#define BUFFER_LEN_MAX
Definition import.c:83
static void vidcap_import_finish(void *state)
Definition import.c:585
#define MOD_NAME
Definition import.c:91
static void vidcap_import_probe(struct device_info **available_cards, int *count, void(**deleter)(void *))
Definition import.c:202
static int get_tile_count(const char *directory, codec_t color_spec, char *tile_delim)
Definition import.c:337
static bool init_audio(struct vidcap_import_state *s, const char *audio_filename)
Definition import.c:211
static void vidcap_import_done(void *state)
Definition import.c:631
static void reset_import(struct vidcap_import_state *s)
Definition import.c:1023
seek_direction_t
Definition import.c:120
@ IMPORT_SEEK_CUR
Definition import.c:122
@ IMPORT_SEEK_SET
Definition import.c:121
#define MAX_NUMBER_WORKERS
Definition import.c:90
static void vidcap_import_new_message(struct module *)
Definition import.c:669
#define ALLOC_ALIGN
Definition import.c:837
static void * aligned_malloc(size_t size, size_t alignment)
#define aligned_free
@ LIBRARY_CLASS_VIDEO_CAPTURE
Definition lib_common.h:78
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
Definition lib_common.h:144
#define MIN(a, b)
Definition macros.h:63
#define MAX(a, b)
Definition macros.h:65
void free_message(struct message *msg, struct response *r)
struct response * new_response(int status, const char *text)
struct message * check_message(struct module *mod)
#define RESPONSE_ACCEPTED
Definition messaging.h:58
void playback_register_keyboard_ctl(struct module *mod)
Definition playback.c:102
int ring_get_size(struct ring_buffer *ring)
int ring_get_current_size(struct ring_buffer *ring)
void ring_buffer_destroy(struct ring_buffer *ring)
struct ring_buffer * ring_buffer_init(int size)
void ring_buffer_flush(struct ring_buffer *buf)
int ring_buffer_read(struct ring_buffer *ring, char *out, int max_len)
void ring_buffer_write(struct ring_buffer *ring, const char *in, int len)
struct msghdr msg
const char * name
Definition settings.cpp:193
struct used to comunicate with audio capture/playback device
Definition types.h:99
int sample_rate
Definition types.h:101
char * data
Definition types.h:102
int ch_count
Definition types.h:104
int data_len
Definition types.h:103
int bps
Definition types.h:100
int max_size
Definition types.h:105
int video_frames_played
Definition import.c:150
long long int played_samples
Definition import.c:149
struct wav_metadata metadata
Definition import.c:139
pthread_cond_t worker_cv
Definition import.c:145
pthread_mutex_t lock
Definition import.c:148
ring_buffer_t * data
Definition import.c:140
struct message_queue message_queue
Definition import.c:152
FILE * file
Definition import.c:138
int total_samples
Definition import.c:141
pthread_cond_t boss_cv
Definition import.c:146
bool has_audio
Definition import.c:137
int samples_read
Definition import.c:142
pthread_t thread_id
Definition import.c:143
bool pipe
Definition import.c:756
struct client * next
int fd
Definition import.c:753
char buff[1024]
struct import_message * next
Definition import.c:117
void * data
Definition import.c:115
size_t data_len
Definition import.c:116
import_message_t type
Definition import.c:114
struct import_message * tail
Definition import.c:132
size_t len
Definition import.c:133
struct import_message * head
Definition import.c:131
enum module_class cls
Definition module.h:115
void * priv_data
Definition module.h:126
notify_t new_message
if set, notifies module that new message is in queue, receiver lock is hold during the call
Definition module.h:119
struct message m
Definition messaging.h:156
Definition import.c:99
int count
Definition import.c:101
struct tile_data tiles[]
Definition import.c:102
struct processed_entry * next
Definition import.c:100
ssize_t offset
Definition import.c:127
seek_direction_t whence
Definition import.c:126
char * data
Definition import.c:95
int data_len
Definition import.c:96
char * data
Raw tile data. Pointer must be at least 4B aligned.
Definition types.h:287
unsigned int data_len
length of the data
Definition types.h:288
pthread_cond_t boss_cv
Definition import.c:168
bool should_exit_at_end
Definition import.c:182
struct processed_entry *volatile *volatile tail
Definition import.c:169
pthread_cond_t worker_cv
Definition import.c:167
struct audio_frame audio_frame
Definition import.c:158
struct message_queue message_queue
Definition import.c:164
struct module * parent
Definition import.c:157
pthread_t video_thread_id
Definition import.c:172
pthread_mutex_t lock
Definition import.c:166
volatile int queue_len
Definition import.c:170
struct audio_state audio_state
Definition import.c:159
struct video_desc video_desc
Definition import.c:160
int video_reading_threads_count
Definition import.c:181
struct module mod
Definition import.c:156
struct timeval prev_time
Definition import.c:174
struct processed_entry *volatile head
Definition import.c:169
unsigned int tile_count
Definition types.h:185
codec_t color_spec
Definition types.h:182
enum interlacing_t interlacing
Definition types.h:184
double fps
Definition types.h:183
unsigned int width
width of every tile (!)
Definition types.h:179
unsigned int height
height of every tile (!)
Definition types.h:180
void(* dispose)(struct video_frame *)
Definition types.h:245
Struct video_frame represents a video frame and contains video description.
Definition types.h:305
struct tile tiles[FLEXIBLE_ARRAY_MEMBER]
Definition types.h:345
struct video_frame_callbacks callbacks
Definition types.h:324
char file_name_prefix[512]
Definition import.c:829
struct processed_entry * entry
Definition import.c:833
unsigned int tile_count
Definition import.c:832
char file_name_suffix[512]
Definition import.c:830
unsigned int ch_count
Definition wav_reader.h:47
unsigned int sample_rate
Definition wav_reader.h:48
unsigned int bits_per_sample
Definition wav_reader.h:49
long long data_size
size of data, -1 if unknown
Definition wav_reader.h:54
uint32_t ts
Definition tfrc.c:101
uint32_t tv_diff_usec(struct timeval curr_time, struct timeval prev_time)
Definition tv.c:109
void ts_add_nsec(struct timespec *ts, long long offset)
Definition tv.c:79
int timespec_get(struct timespec *ts, int base)
Definition tv.c:236
#define NS_IN_SEC
Definition tv.h:92
#define TIME_UTC
Definition tv.h:116
interlacing_t
Definition types.h:163
@ INTERLACING_MAX
Definition types.h:169
codec_t
Definition types.h:65
@ VIDEO_CODEC_NONE
dummy color spec
Definition types.h:66
This is an umbrella header for video functions.
struct module * vidcap_params_get_parent(const struct vidcap_params *params)
const char * vidcap_params_get_fmt(const struct vidcap_params *params)
unsigned int vidcap_params_get_flags(const struct vidcap_params *params)
#define VIDCAP_FLAG_AUDIO_EMBEDDED
HD-SDI embedded audio.
const char * get_codec_file_extension(codec_t codec)
codec_t get_codec_from_fcc(uint32_t fourcc)
#define VIDEO_EXPORT_SUMMARY_VERSION
int wav_seek(FILE *wav_file, long offset, int whence, struct wav_metadata *metadata)
same as fseek but WAV header presence is respected (for SEEK SET)
Definition wav_reader.c:319
const char * get_wav_error(int errcode)
Definition wav_reader.c:300
size_t wav_read(void *buffer, size_t sample_count, FILE *wav_file, struct wav_metadata *metadata)
Definition wav_reader.c:291
int read_wav_header(FILE *wav_file, struct wav_metadata *metadata)
Definition wav_reader.c:215
#define WAV_HDR_PARSE_OK
Definition wav_reader.h:57
void * wait_task(task_result_handle_t handle)
Definition worker.cpp:280
task_result_handle_t task_run_async(runnable_t task, void *data)
Runs task asynchronously.
Definition worker.cpp:262
void * task_result_handle_t
Definition worker.h:49