UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
gpustitch.cpp
Go to the documentation of this file.
1
8/*
9 * Copyright (c) 2021-2023 CESNET z.s.p.o.
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, is permitted provided that the following conditions
14 * are met:
15 *
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 *
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 *
23 * 3. Neither the name of CESNET nor the names of its contributors may be
24 * used to endorse or promote products derived from this software without
25 * specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
28 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
29 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
30 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
31 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
32 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
33 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
34 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
37 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
38 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 */
40#include "config.h"
41#include "config_unix.h"
42#include "config_win32.h"
43
44#include "debug.h"
45#include "host.h"
46#include "lib_common.h"
47#include "video.h"
48#include "video_capture.h"
49
50#include "tv.h"
51
52#include "audio/types.h"
53
54#include <stdio.h>
55#include <stdlib.h>
56#include <vector>
57#include <cmath>
58#include <limits>
59#include <thread>
60#include <condition_variable>
61#include <libgpustitch/stitcher.hpp>
62#include <libgpustitch/config_utils.hpp>
63#include <cuda.h>
64#include <cuda_runtime.h>
65
66#include "utils/cuda_pix_conv.h"
68
69#define PI 3.14159265
70
71const static char *log_str = "[gpustitch] ";
72
73/* prototypes of functions defined in this module */
74static void show_help(void);
75
76static double toRadian(double deg){
77 return (deg / 180) * 3.14159265;
78}
79
80static void show_help()
81{
82 color_printf("Gpustitch video stitcher\n");
83 color_printf("Stitches a 360 degree panorama from multiple cameras\n");
84 color_printf("Usage\n");
85 color_printf(TERM_BOLD TERM_FG_RED "\t-t gpustitch" TERM_FG_RESET ":rig_spec=<filepath>[:width=<w>][:blend_algo=<multiband|feather>][:multiband_levels=<n>][:feather_width=<w>][:tiled] -t <dev1_config> -t <dev2_config> ....\n");
86 color_printf(TERM_BOLD"\t\t<devn_config>" TERM_RESET " - a complete configuration string of device involved in stitching\n");
87 color_printf(TERM_BOLD"\t\t<rig_spec>" TERM_RESET " - file containing rig calibration parameters\n");
88 color_printf(TERM_BOLD"\t\t<blend_algo>" TERM_RESET " - algorithm for seam blending. Multiband is nicer but slower\n");
89 color_printf(TERM_BOLD"\t\t<multiband_levels>" TERM_RESET " - number of levels to use when multiband blending is used (default: 4)\n");
90 color_printf(TERM_BOLD"\t\t<feather_width>" TERM_RESET " - blending width when feather blending is used (default: 30)\n");
91 color_printf(TERM_BOLD"\t\ttiled" TERM_RESET " - Use if input is a tiled image from one capture device\n");
92
93}
94
96
98 unsigned devices_cnt;
99
103 struct timeval t, t0;
104
105 gpustitch::Stitcher stitcher;
106 gpustitch::Stitcher_params stitch_params;
107 std::vector<gpustitch::Cam_params> cam_properties;
108
109 std::string spec_path;
110 double fps;
112 bool tiled_capture = false;
113 bool output_cuda_buf = false;
114
115 unsigned char *conv_tmp_frame = nullptr;
116 void (*conv_func)(unsigned char *dst,
117 size_t dstPitch,
118 unsigned char *src,
119 size_t srcPitch,
120 size_t width,
121 size_t height,
122 struct CUstream_st *stream);
123
124 std::mutex stitched_mut;
125 unsigned stitched_count = 0;
126 bool done = false;
127 std::condition_variable stitched_cv;
128
129 std::vector<grab_worker_state> capture_workers;
130};
131
133 std::thread thread;
134 std::mutex grabbed_mut;
135 unsigned grabbed_count = 0;
136 double grabbed_fps = -1;
137 std::condition_variable grabbed_cv;
138
140
141 bool tiled = false;
142
143 unsigned width = 0;
144 unsigned height = 0;
145 unsigned char *tmp_in_frame;
146 unsigned char *tmp_rgba_frame;
148 void (*conv_func)(unsigned char *dst,
149 size_t dstPitch,
150 unsigned char *src,
151 size_t srcPitch,
152 size_t width,
153 size_t height,
154 struct CUstream_st *stream);
156
158};
159
160static void vidcap_gpustitch_probe(device_info **available_cards, int *count, void (**deleter)(void *))
161{
162 *deleter = free;
163 *available_cards = nullptr;
164 *count = 0;
165 return;
166}
167
168static bool check_in_format(grab_worker_state *gs, video_frame *in, int i){
169 if(in->tile_count != 1){
170 std::cerr << log_str << "Only frames with tile_count == 1 are supported" << std::endl;
171 return false;
172 }
173
174 unsigned int expected_w = gs->width;
175 unsigned int expected_h = gs->height;
176
177 if(in->tiles[0].width != expected_w
178 || in->tiles[0].height != expected_h)
179 {
180 std::cerr << log_str << "Wrong resolution for input " << i << "!"
181 << " Expected " << expected_w << "x" << expected_h
182 << ", but got " << in->tiles[0].width << "x" << in->tiles[0].height << std::endl;
183 return false;
184 }
185
186 if(gs->in_codec != in->color_spec){
187 printf("New codec detected! RECONF\n");
188 assert( in->color_spec == RGB
189 || in->color_spec == RGBA
190 || in->color_spec == UYVY
191 );
192 gs->in_codec = in->color_spec;
194 gs->tmp_in_frame = nullptr;
195
196 int in_line_size = vc_get_linesize(expected_w, in->color_spec);
197 cudaMalloc(&gs->tmp_in_frame, in_line_size * expected_h);
198 switch(in->color_spec){
199 case RGB:
201 break;
202 case UYVY:
204 break;
205 default:
206 gs->conv_func = nullptr;
207 }
208 }
209
210 return true;
211}
212
214 size_t x_offset, size_t y_offset, size_t w, size_t h,
215 unsigned char *dst,
216 unsigned dst_pitch,
217 cudaStream_t stream)
218{
219 unsigned int width = in_frame->tiles[0].width;
220 int in_line_size = vc_get_linesize(width, in_frame->color_spec);
221 int roi_line_size = vc_get_linesize(w, in_frame->color_spec);
222
223 size_t offset_bytes = y_offset * in_line_size
224 + vc_get_linesize(x_offset, in_frame->color_spec);
225
226 w = (w / get_pf_block_bytes(in_frame->color_spec)) * get_pf_block_bytes(in_frame->color_spec);
227
228 if(gs->conv_func){
229 if (cudaMemcpy2DAsync(gs->tmp_in_frame + offset_bytes,
230 in_line_size,
231 (unsigned char *)(in_frame->tiles[0].data) + offset_bytes,
232 in_line_size,
233 roi_line_size, h,
235 {
236 std::cerr << "Error copying input image bitmap to CUDA buffer" << std::endl;
237 return false;
238 }
239
240 gs->conv_func(dst + y_offset * dst_pitch + vc_get_linesize(x_offset, RGBA),
241 dst_pitch,
242 gs->tmp_in_frame + offset_bytes, in_line_size,
243 w, h, stream);
244 } else {
245 if (cudaMemcpy2D(dst + y_offset * dst_pitch + vc_get_linesize(x_offset, RGBA), dst_pitch,
246 (unsigned char*)(in_frame->tiles[0].data) + offset_bytes,
247 in_line_size,
248 w, h,
250 {
251 std::cerr << "Error copying RGBA image bitmap to CUDA buffer" << std::endl;
252 return false;
253
254 }
255 }
256
257 return true;
258}
259
260static bool upload_frame(grab_worker_state *gs, video_frame *in_frame, int i){
261 cudaStream_t stream;
262 gs->s->stitcher.get_input_stream(i, &stream);
263
264 if(!stream){
265 std::cerr << std::endl << "Failed to get input stream." << std::endl;
266 return false;
267 }
268
270 in_frame,
271 0, 0, gs->width, gs->height,
272 gs->tmp_rgba_frame,
274 stream);
275
276 gs->s->stitcher.submit_input_image_async(i, gs->tmp_rgba_frame,
277 gs->width, gs->height,
279 gpustitch::Src_mem_kind::Device);
280
281 return true;
282}
283
285 const int order[] = {3, 1, 2, 0};
286
287 for(int i = 0; i < 4; i++){
288 cudaStream_t stream;
289
290 gs->s->stitcher.get_input_stream(i, &stream);
291
292 unsigned tile_height = gs->height / 2;
293 unsigned tile_width = gs->width / 2;
294 unsigned src_pitch = gs->tmp_rgba_frame_pitch;
295 unsigned tile_line_len = gs->tmp_rgba_frame_pitch / 2;
296 unsigned x_offset = (order[i] % 2) * tile_width;
297 unsigned y_offset = (order[i] / 2) * tile_height;
298
300 frame,
301 x_offset, y_offset, tile_width, tile_height,
302 gs->tmp_rgba_frame,
304 stream
305 );
306
307 unsigned char *src = static_cast<unsigned char *>(gs->tmp_rgba_frame);
308 src += (order[i] % 2) * tile_line_len;
309 src += (order[i] / 2) * src_pitch * tile_height;
310
311 gs->s->stitcher.submit_input_image_async(i, src,
312 gs->width / 2, gs->height / 2,
314 gpustitch::Src_mem_kind::Device);
315
316 }
317}
318
321 struct audio_frame *audio_frame = NULL;
322 struct video_frame *frame = NULL;
323 std::unique_lock<std::mutex> stitch_lk(gs->s->stitched_mut, std::defer_lock);
324 std::unique_lock<std::mutex> grab_lk(gs->grabbed_mut, std::defer_lock);
325
326 gs->width = gs->s->cam_properties[id].width;
327 gs->height = gs->s->cam_properties[id].height;
328 if(gs->tiled){
329 gs->width *= 2;
330 gs->height *= 2;
331 }
332
333 int in_line_size = vc_get_linesize(gs->width, RGBA);
334 gs->tmp_rgba_frame_pitch = in_line_size;
335 cudaMalloc(&gs->tmp_rgba_frame, in_line_size * gs->height);
336
337 vidcap *device = nullptr;
338
339 int ret = initialize_video_capture(NULL, param, &device);
340 if(ret != 0) {
341 fprintf(stderr, "[gpustitch] Unable to initialize device %d (%s:%s).\n",
344 return;
345 }
346
347 cudaStreamCreate(&gs->tmp_in_frame_stream);
348
349 while(true){
350 stitch_lk.lock();
351 PROFILE_DETAIL("Wait for cv");
352 gs->s->stitched_cv.wait(stitch_lk,
353 [gs]{return gs->s->stitched_count == gs->grabbed_count || gs->s->done;});
354 if(gs->s->done){
356 vidcap_done(device);
357 cudaStreamDestroy(gs->tmp_in_frame_stream);
358 return;
359 }
360 stitch_lk.unlock();
361
363
364 frame = nullptr;
365 PROFILE_DETAIL("Grab frame");
366 while(!frame){
367 frame = vidcap_grab(device, &audio_frame);
368 }
369
370 if (check_in_format(gs, frame, id)){
371 PROFILE_DETAIL("Upload");
372 if(gs->tiled){
373 upload_tiles(gs, frame);
374
375 } else {
376 upload_frame(gs, frame, id);
377 }
378 }
379
380 grab_lk.lock();
381 gs->grabbed_count += 1;
382 if(frame)
383 gs->grabbed_fps = frame->fps;
384 grab_lk.unlock();
385 gs->grabbed_cv.notify_one();
386 }
387}
388
390 switch(s->out_fmt){
391 case RGBA:
392 s->conv_func = nullptr;
393 break;
394 case RGB:
396 break;
397 case UYVY:
399 break;
400 default:
401 assert(false && "Unsupported output pixel format!");
402 }
403
404 int num_gpus;
405 cudaGetDeviceCount(&num_gpus);
406
407 int selected_gpu = -1;
408
409 for (int gpu = 0; gpu < num_gpus; ++gpu)
410 {
411 cudaDeviceProp prop;
412 cudaGetDeviceProperties(&prop, gpu);
413
414 // Require minimum compute 5.2
415 if (prop.major > 5 || (prop.major == 5 && prop.minor >= 2))
416 {
417 selected_gpu = gpu;
418 break;
419 }
420 }
421 if (selected_gpu == -1) {
422 log_msg(LOG_LEVEL_ERROR, "Cannot find any usable CUDA device!\n");
423 return false;
424 }
425 cudaSetDevice(selected_gpu);
426
427 gpustitch::read_params(s->spec_path, s->stitch_params, s->cam_properties);
428
429 if(s->cam_properties.size() != s->devices_cnt && !(s->tiled_capture && s->devices_cnt == 1)){
430 log_msg(LOG_LEVEL_ERROR, "Number of capture devices is different from specified number of cameras!\n");
431 if(s->cam_properties.size() < s->devices_cnt)
432 return false;
433 }
434
435 try{
436 s->stitcher = gpustitch::Stitcher(s->stitch_params, s->cam_properties);
437 } catch(char const *exc){
438 log_msg(LOG_LEVEL_ERROR, "Exception while initializing stitcher: %s\n", exc);
439 return false;
440 }
441
442 return true;
443}
444
445static void parse_fmt(vidcap_gpustitch_state *s, const char * const fmt){
446 s->fps = -1;
447 s->out_fmt = RGBA;
448
449 s->stitch_params.width = 3840;
450 s->stitch_params.height = s->stitch_params.width / 2;
451 s->stitch_params.blend_algorithm = gpustitch::Blend_algorithm::Multiband;
452 s->stitch_params.feather_width = 30;
453 s->stitch_params.multiband_levels = 4;
454
455
456 if(!fmt)
457 return;
458
459 char *tmp = strdup(fmt);
460 char *init_fmt = tmp;
461 char *save_ptr = NULL;
462 char *item;
463#define FMT_CMP(param) (strncmp(item, (param), strlen((param))) == 0)
464 while((item = strtok_r(init_fmt, ":", &save_ptr))) {
465 if (FMT_CMP("width=")) {
466 s->stitch_params.width = atoi(strchr(item, '=') + 1);
467 s->stitch_params.height = s->stitch_params.width / 2;
468 } else if(FMT_CMP("blend_algo=")){
469 struct {
470 const char *name;
471 gpustitch::Blend_algorithm algo;
472 } blend_algos[] = {
473 {"multiband", gpustitch::Blend_algorithm::Multiband},
474 {"feather", gpustitch::Blend_algorithm::Feather},
475 };
476
477 const char *req = strchr(item, '=') + 1;
478
479 bool selected = false;
480 for(const auto& i : blend_algos){
481 if(strcmp(req, i.name) == 0){
482 s->stitch_params.blend_algorithm = i.algo;
483 selected = true;
484 break;
485 }
486 }
487
488 if(!selected){
489 log_msg(LOG_LEVEL_WARNING, "Invalid blend algorithm, falling back to default\n");
490 }
491 } else if(FMT_CMP("feather_width=")){
492 s->stitch_params.feather_width = atoi(strchr(item, '=') + 1);
493 } else if(FMT_CMP("multiband_levels=")){
494 s->stitch_params.multiband_levels = atoi(strchr(item, '=') + 1);
495 } else if(FMT_CMP("rig_spec=")){
496 s->spec_path = strchr(item, '=') + 1;
497 } else if(FMT_CMP("fps=")){
498 s->fps = strtod(strchr(item, '=') + 1, nullptr);
499 } else if(FMT_CMP("fmt=")){
500 s->out_fmt = get_codec_from_name(strchr(item, '=') + 1);
501 } else if(FMT_CMP("tiled")){
502 s->tiled_capture = true;
503 } else if(FMT_CMP("cudabuf")){
504 s->output_cuda_buf = true;
505 }
506 init_fmt = NULL;
507 }
508 free(tmp);
509}
510
512 std::unique_lock<std::mutex> lk(s->stitched_mut);
513 s->done = true;
514 lk.unlock();
515
516 s->stitched_cv.notify_all();
517
518 for(auto& worker : s->capture_workers){
519 if(worker.thread.joinable())
520 worker.thread.join();
521 }
522}
523
524static int
526{
527 printf("vidcap_gpustitch_init\n");
528
531 }
532
534 if(s == NULL) {
535 printf("Unable to allocate gpustitch capture state\n");
536 return VIDCAP_INIT_FAIL;
537 }
538
539 s->frames = 0;
540 gettimeofday(&s->t0, NULL);
541
542 if(vidcap_params_get_fmt(params) && strcmp(vidcap_params_get_fmt(params), "help") == 0) {
543 show_help();
544 delete s;
545 return VIDCAP_INIT_NOERR;
546 }
547
549
550 s->devices_cnt = 0;
551 struct vidcap_params *tmp = params;
552 while((tmp = vidcap_params_get_next(tmp))) {
553 if (vidcap_params_get_driver(tmp) != NULL)
554 s->devices_cnt++;
555 else
556 break;
557 }
558
559 if(s->tiled_capture && s->devices_cnt != 1){
560 log_msg(LOG_LEVEL_ERROR, "Number of capture devices must be exactly 1 when using tiled capture!\n");
561 return VIDCAP_INIT_FAIL;
562 }
563
564 s->captured_frames = (struct video_frame **) calloc(s->devices_cnt, sizeof(struct video_frame *));
565
566 s->frame = nullptr;
567
568 if(!init_stitcher(s)){
569 goto error;
570 }
571
572 s->capture_workers = std::vector<grab_worker_state>(s->devices_cnt);
573 tmp = params;
574 for (unsigned i = 0; i < s->devices_cnt; ++i) {
575 tmp = vidcap_params_get_next(tmp);
576
577 s->capture_workers[i].s = s;
578 s->capture_workers[i].tiled = s->tiled_capture;
579 s->capture_workers[i].thread =
580 std::thread(grab_worker, &s->capture_workers[i], tmp, i);
581 }
582
583
584
585 *state = s;
586 return VIDCAP_INIT_OK;
587
588error:
590 delete s;
591 return VIDCAP_INIT_FAIL;
592}
593
594static void
596{
598
599 if(!s) return;
600
602
603 free(s->captured_frames);
604 vf_free(s->frame);
606
607 delete s;
608}
609
610static void result_data_delete(struct video_frame *buf){
611 if(!buf)
612 return;
613
614 cudaFreeHost(buf->tiles[0].data);
615}
616
617static bool allocate_result_frame(vidcap_gpustitch_state *s, unsigned width, unsigned height){
619 desc.width = width;
620 desc.height = height;
622 desc.tile_count = 1;
623 desc.fps = s->fps;
624
628
629 s->frame->callbacks.data_deleter = NULL;
630 s->frame->callbacks.recycle = NULL;
631
632 if(!s->output_cuda_buf){
633 if(cudaMallocHost(&s->frame->tiles[0].data, s->frame->tiles[0].data_len) != cudaSuccess){
634 std::cerr << log_str << "Failed to allocate result frame" << std::endl;
635 return false;
636 }
638 }
639
640 if(s->conv_func){
641 size_t size = vc_get_linesize(desc.width, s->out_fmt) * desc.height;
642
643 if(cudaMalloc(&s->conv_tmp_frame, size) != cudaSuccess){
644 std::cerr << log_str << "Failed to allocate tmp conv frame" << std::endl;
645 return false;
646 }
647 }
648
649 return true;
650}
651
652static bool download_stitched(vidcap_gpustitch_state *s, cudaStream_t out_stream){
654 gpustitch::Image_cuda *output_image;
655
656 output_image = s->stitcher.get_output_image();
657 if(!output_image){
658 std::cerr << log_str << "Failed to get output buffer" << std::endl;
659 return false;
660 }
661
662 size_t w = output_image->get_width();
663 size_t h = output_image->get_height();
664
665 if(!s->frame){
666 if(!allocate_result_frame(s, w, h)){
667 return false;
668 }
669 }
670
671 void *src = output_image->data();
672 size_t src_pitch = output_image->get_pitch();
673 size_t row_bytes = output_image->get_row_bytes();
674 if(s->conv_func){
676 (unsigned char *) src, src_pitch,
677 w, h,
678 out_stream);
679 src = s->conv_tmp_frame;
680 src_pitch = vc_get_linesize(w, s->out_fmt);
681 row_bytes = src_pitch;
682 }
683
684 if(s->output_cuda_buf){
685 s->frame->tiles[0].data = (char *) src;
686 } else {
687 if (cudaMemcpy2DAsync(s->frame->tiles[0].data, row_bytes,
688 src, src_pitch,
689 row_bytes, h,
690 cudaMemcpyDeviceToHost, out_stream) != cudaSuccess)
691 {
692 std::cerr << log_str << "Error copying output panorama from CUDA buffer" << std::endl;
693 return false;
694 }
695 }
696
697 return true;
698}
699
701 gettimeofday(&s->t, NULL);
702 double seconds = tv_diff(s->t, s->t0);
703 if (seconds >= 5) {
704 float fps = s->frames / seconds;
705 log_msg(LOG_LEVEL_INFO, "[gpustitch cap.] %d frames in %g seconds = %g FPS\n", s->frames, seconds, fps);
706 s->t0 = s->t;
707 s->frames = 0;
708 }
709}
710
713 double fps = std::numeric_limits<double>::max();
714 for (auto& worker : s->capture_workers) {
715 std::unique_lock<std::mutex> lk(worker.grabbed_mut);
716 worker.grabbed_cv.wait(lk, [&]{return worker.grabbed_count > s->stitched_count;});
717 if(worker.grabbed_fps != -1 && worker.grabbed_fps < fps)
718 fps = worker.grabbed_fps;
719 }
720
721 return fps;
722}
723
724static struct video_frame *stitch(struct vidcap_gpustitch_state *s){
726 s->stitcher.stitch();
727 if(false){
728 std::cout << std::endl << "Failed to stitch." << std::endl;
729 return NULL;
730 }
731
732 cudaStream_t out_stream;
733 s->stitcher.get_output_stream(&out_stream);
734
735 if(!out_stream){
736 std::cerr << log_str << "Failed to get output stream" << std::endl;
737 return NULL;
738 }
739
740 std::unique_lock<std::mutex> stitch_lk(s->stitched_mut);
741 s->stitched_count += 1;
742 stitch_lk.unlock();
743 s->stitched_cv.notify_all();
744
745 if(!download_stitched(s, out_stream)){
746 return NULL;
747 }
748
749 PROFILE_DETAIL("Synchronize 2");
750 if (cudaStreamSynchronize(out_stream) != cudaSuccess)
751 {
752 std::cerr << "Error synchronizing with the output CUDA stream" << std::endl;
753 return NULL;
754 }
755
756 return s->frame;
757}
758
759static struct video_frame *
760vidcap_gpustitch_grab(void *state, struct audio_frame **audio [[maybe_unused]])
761{
764
765 video_frame *f = nullptr;
766 double fps = wait_for_frames(s);
767
768 f = stitch(s);
769 if(!f){
770 return nullptr;
771 }
772
773 //If s->fps == -1 it means it was not set by user so we use the fps
774 //of the slowest grabber
775 if(s->fps <= 0){
776 f->fps = fps;
777 } else {
778 f->fps = s->fps;
779 }
780
781 s->frames++;
782 report_stats(s);
783
784 return f;
785}
786
794
796
void ** state
struct video_desc desc
struct video_frame * frame
const char * fmt
struct audio_frame ** audio
int void(** deleter)(void *))
void vf_free(struct video_frame *buf)
Frees video_frame structure.
int vc_get_linesize(unsigned int width, codec_t codec)
struct video_frame * vf_alloc_desc(struct video_desc desc)
Allocates video frame accordig given desc.
codec_t get_codec_from_name(const char *name)
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
void cuda_RGBA_to_UYVY(unsigned char *dst, size_t dstPitch, unsigned char *src, size_t srcPitch, size_t width, size_t height, CUstream_st *stream)
void cuda_UYVY_to_RGBA(unsigned char *dst, size_t dstPitch, unsigned char *src, size_t srcPitch, size_t width, size_t height, CUstream_st *stream)
void cuda_RGBA_to_RGB(unsigned char *dst, size_t dstPitch, unsigned char *src, size_t srcPitch, size_t width, size_t height, CUstream_st *stream)
void cuda_RGB_to_RGBA(unsigned char *dst, size_t dstPitch, unsigned char *src, size_t srcPitch, size_t width, size_t height, CUstream_st *stream)
#define cudaSuccess
#define cudaFree
#define cudaMemcpyHostToDevice
#define cudaMalloc
#define cudaFreeHost
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_INFO
normal reporting
Definition debug.h:62
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
static void vidcap_gpustitch_done(void *state)
static int vidcap_gpustitch_init(struct vidcap_params *params, void **state)
static void report_stats(vidcap_gpustitch_state *s)
static bool check_in_format(grab_worker_state *gs, video_frame *in, int i)
static void stop_grab_workers(vidcap_gpustitch_state *s)
static void vidcap_gpustitch_probe(device_info **available_cards, int *count, void(**deleter)(void *))
static const struct video_capture_info vidcap_gpustitch_info
static bool allocate_result_frame(vidcap_gpustitch_state *s, unsigned width, unsigned height)
static void upload_tiles(grab_worker_state *gs, video_frame *frame)
static double wait_for_frames(struct vidcap_gpustitch_state *s)
static bool upload_to_cuda_buf(grab_worker_state *gs, video_frame *in_frame, size_t x_offset, size_t y_offset, size_t w, size_t h, unsigned char *dst, unsigned dst_pitch, cudaStream_t stream)
static void result_data_delete(struct video_frame *buf)
static void parse_fmt(vidcap_gpustitch_state *s, const char *const fmt)
static bool upload_frame(grab_worker_state *gs, video_frame *in_frame, int i)
static struct video_frame * vidcap_gpustitch_grab(void *state, struct audio_frame **audio)
static const char * log_str
Definition gpustitch.cpp:71
static bool init_stitcher(struct vidcap_gpustitch_state *s)
static bool download_stitched(vidcap_gpustitch_state *s, cudaStream_t out_stream)
static struct video_frame * stitch(struct vidcap_gpustitch_state *s)
static void show_help(void)
Definition gpustitch.cpp:80
static void grab_worker(grab_worker_state *gs, vidcap_params *param, int id)
#define FMT_CMP(param)
static double toRadian(double deg)
Definition gpustitch.cpp:76
#define VIDCAP_INIT_NOERR
state not initialized, other action performed (typically help)
#define VIDCAP_INIT_OK
initialization successful
struct video_frame * vidcap_grab(struct vidcap *state, struct audio_frame **audio)
Grabs video frame. This function may block for a short period if waiting for incoming frame....
#define VIDCAP_NO_GENERIC_FPS_INDICATOR
int initialize_video_capture(struct module *parent, struct vidcap_params *param, struct vidcap **state)
Initializes video capture.
void vidcap_done(struct vidcap *state)
Destroys vidap state.
#define VIDEO_CAPTURE_ABI_VERSION
#define VIDCAP_INIT_FAIL
error ocured
#define VIDCAP_INIT_AUDIO_NOT_SUPPORTED
card does not support audio
int count
Definition hd-rum.c:44
static struct @19 params[100]
@ 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
static void * worker(void *args)
Definition alsa.c:190
#define PROFILE_FUNC
#define PROFILE_DETAIL(name)
const char * param
Definition settings.cpp:195
const char * name
Definition settings.cpp:193
struct used to comunicate with audio capture/playback device
Definition types.h:99
std::thread thread
cudaStream_t tmp_in_frame_stream
unsigned grabbed_count
vidcap_gpustitch_state * s
unsigned tmp_rgba_frame_pitch
void(* conv_func)(unsigned char *dst, size_t dstPitch, unsigned char *src, size_t srcPitch, size_t width, size_t height, struct CUstream_st *stream)
std::condition_variable grabbed_cv
std::mutex grabbed_mut
unsigned char * tmp_in_frame
unsigned char * tmp_rgba_frame
Definition hd-rum.c:27
unsigned int width
Definition types.h:280
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
unsigned int height
Definition types.h:281
std::vector< gpustitch::Cam_params > cam_properties
unsigned char * conv_tmp_frame
std::vector< grab_worker_state > capture_workers
gpustitch::Stitcher_params stitch_params
std::condition_variable stitched_cv
struct timeval t t0
struct video_frame * frame
void(* conv_func)(unsigned char *dst, size_t dstPitch, unsigned char *src, size_t srcPitch, size_t width, size_t height, struct CUstream_st *stream)
struct video_frame ** captured_frames
gpustitch::Stitcher stitcher
This struct represents video capture state.
unsigned int tile_count
Definition types.h:185
codec_t color_spec
Definition types.h:182
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(* data_deleter)(struct video_frame *)
Definition types.h:256
void(* recycle)(struct video_frame *)
Definition types.h:263
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
double fps
Definition types.h:308
unsigned int tile_count
Definition types.h:344
struct video_frame_callbacks callbacks
Definition types.h:324
codec_t color_spec
Definition types.h:306
double tv_diff(struct timeval curr_time, struct timeval prev_time)
Definition tv.c:97
codec_t
Definition types.h:65
@ RGB
RGB 8-bit (packed into 24-bit word)
Definition types.h:80
@ UYVY
YCbCr 422 8-bit - Cb Y0 Cr Y1.
Definition types.h:71
@ VIDEO_CODEC_NONE
dummy color spec
Definition types.h:66
@ RGBA
RGBA 8-bit, big-endian.
Definition types.h:70
This is an umbrella header for video functions.
struct vidcap_params * vidcap_params_get_next(const struct vidcap_params *curr)
const char * vidcap_params_get_driver(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_ANY
int get_pf_block_bytes(codec_t codec)
returns codec_info_t::block_size_bytes
#define VIDEO_FRAME_DISPOSE(frame)
Definition video_frame.h:76