UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
gpustitch.cpp File Reference

Gpustitch 360 video stitcher. More...

#include "config.h"
#include "config_unix.h"
#include "config_win32.h"
#include "debug.h"
#include "host.h"
#include "lib_common.h"
#include "video.h"
#include "video_capture.h"
#include "tv.h"
#include "audio/types.h"
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <cmath>
#include <limits>
#include <thread>
#include <condition_variable>
#include <libgpustitch/stitcher.hpp>
#include <libgpustitch/config_utils.hpp>
#include <cuda.h>
#include <cuda_runtime.h>
#include "utils/cuda_pix_conv.h"
#include "utils/profile_timer.hpp"
Include dependency graph for gpustitch.cpp:

Go to the source code of this file.

Classes

struct  grab_worker_state
 
struct  vidcap_gpustitch_state
 

Macros

#define FMT_CMP(param)
 
#define PI   3.14159265
 

Functions

static bool allocate_result_frame (vidcap_gpustitch_state *s, unsigned width, unsigned height)
 
static bool check_in_format (grab_worker_state *gs, video_frame *in, int i)
 
static bool download_stitched (vidcap_gpustitch_state *s, cudaStream_t out_stream)
 
static void grab_worker (grab_worker_state *gs, vidcap_params *param, int id)
 
static bool init_stitcher (struct vidcap_gpustitch_state *s)
 
static void parse_fmt (vidcap_gpustitch_state *s, const char *const fmt)
 
 REGISTER_MODULE (gpustitch, &vidcap_gpustitch_info, LIBRARY_CLASS_VIDEO_CAPTURE, VIDEO_CAPTURE_ABI_VERSION)
 
static void report_stats (vidcap_gpustitch_state *s)
 
static void result_data_delete (struct video_frame *buf)
 
static void show_help (void)
 
static struct video_frame * stitch (struct vidcap_gpustitch_state *s)
 
static void stop_grab_workers (vidcap_gpustitch_state *s)
 
static double toRadian (double deg)
 
static bool upload_frame (grab_worker_state *gs, video_frame *in_frame, int i)
 
static void upload_tiles (grab_worker_state *gs, video_frame *frame)
 
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 vidcap_gpustitch_done (void *state)
 
static struct video_frame * vidcap_gpustitch_grab (void *state, struct audio_frame **audio)
 
static int vidcap_gpustitch_init (struct vidcap_params *params, void **state)
 
static void vidcap_gpustitch_probe (device_info **available_cards, int *count, void(**deleter)(void *))
 
static double wait_for_frames (struct vidcap_gpustitch_state *s)
 

Variables

static const char * log_str = "[gpustitch] "
 
static const struct video_capture_info vidcap_gpustitch_info
 

Detailed Description

Gpustitch 360 video stitcher.

Author
Martin Piatka piatk.nosp@m.a@ce.nosp@m.snet..nosp@m.cz Martin Pulec pulec.nosp@m.@ces.nosp@m.net.c.nosp@m.z

Definition in file gpustitch.cpp.

Macro Definition Documentation

◆ FMT_CMP

#define FMT_CMP ( param)
Value:
(strncmp(item, (param), strlen((param))) == 0)
const char * param
Definition settings.cpp:195
Definition hd-rum.c:27

Referenced by parse_fmt().

◆ PI

#define PI   3.14159265

Definition at line 69 of file gpustitch.cpp.

Function Documentation

◆ allocate_result_frame()

static bool allocate_result_frame ( vidcap_gpustitch_state * s,
unsigned width,
unsigned height )
static

Definition at line 617 of file gpustitch.cpp.

617 {
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}
struct video_desc desc
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.
#define cudaSuccess
#define cudaMalloc
static void result_data_delete(struct video_frame *buf)
static const char * log_str
Definition gpustitch.cpp:71
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 char * conv_tmp_frame
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)
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 tile tiles[FLEXIBLE_ARRAY_MEMBER]
Definition types.h:345
struct video_frame_callbacks callbacks
Definition types.h:324

References video_frame::callbacks, video_desc::color_spec, vidcap_gpustitch_state::conv_func, vidcap_gpustitch_state::conv_tmp_frame, cudaMalloc, cudaSuccess, tile::data, video_frame_callbacks::data_deleter, tile::data_len, desc, vidcap_gpustitch_state::fps, video_desc::fps, vidcap_gpustitch_state::frame, video_desc::height, log_str, vidcap_gpustitch_state::out_fmt, vidcap_gpustitch_state::output_cuda_buf, video_frame_callbacks::recycle, result_data_delete(), video_desc::tile_count, video_frame::tiles, vc_get_linesize(), vf_alloc_desc(), and video_desc::width.

Referenced by download_stitched().

Here is the call graph for this function:

◆ check_in_format()

static bool check_in_format ( grab_worker_state * gs,
video_frame * in,
int i )
static

Definition at line 168 of file gpustitch.cpp.

168 {
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}
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_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 cudaFree
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)
unsigned char * tmp_in_frame
unsigned int width
Definition types.h:280
unsigned int height
Definition types.h:281
unsigned int tile_count
Definition types.h:344
codec_t color_spec
Definition types.h:306
@ 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
@ RGBA
RGBA 8-bit, big-endian.
Definition types.h:70

References video_frame::color_spec, grab_worker_state::conv_func, cuda_RGB_to_RGBA(), cuda_UYVY_to_RGBA(), cudaFree, cudaMalloc, grab_worker_state::height, tile::height, grab_worker_state::in_codec, log_str, RGB, RGBA, video_frame::tile_count, video_frame::tiles, grab_worker_state::tmp_in_frame, UYVY, vc_get_linesize(), grab_worker_state::width, and tile::width.

Referenced by grab_worker().

Here is the call graph for this function:

◆ download_stitched()

static bool download_stitched ( vidcap_gpustitch_state * s,
cudaStream_t out_stream )
static

Definition at line 652 of file gpustitch.cpp.

652 {
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}
static bool allocate_result_frame(vidcap_gpustitch_state *s, unsigned width, unsigned height)
#define PROFILE_FUNC
gpustitch::Stitcher stitcher

References allocate_result_frame(), vidcap_gpustitch_state::conv_func, vidcap_gpustitch_state::conv_tmp_frame, cudaSuccess, tile::data, vidcap_gpustitch_state::frame, log_str, vidcap_gpustitch_state::out_fmt, vidcap_gpustitch_state::output_cuda_buf, PROFILE_FUNC, vidcap_gpustitch_state::stitcher, video_frame::tiles, and vc_get_linesize().

Referenced by stitch().

Here is the call graph for this function:

◆ grab_worker()

static void grab_worker ( grab_worker_state * gs,
vidcap_params * param,
int id )
static

Definition at line 319 of file gpustitch.cpp.

319 {
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}
struct video_frame * frame
static bool check_in_format(grab_worker_state *gs, video_frame *in, int i)
static void upload_tiles(grab_worker_state *gs, video_frame *frame)
static bool upload_frame(grab_worker_state *gs, video_frame *in_frame, int i)
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....
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 PROFILE_DETAIL(name)
struct used to comunicate with audio capture/playback device
Definition types.h:99
cudaStream_t tmp_in_frame_stream
unsigned grabbed_count
vidcap_gpustitch_state * s
unsigned tmp_rgba_frame_pitch
std::condition_variable grabbed_cv
std::mutex grabbed_mut
unsigned char * tmp_rgba_frame
std::vector< gpustitch::Cam_params > cam_properties
std::condition_variable stitched_cv
This struct represents video capture state.
Struct video_frame represents a video frame and contains video description.
Definition types.h:305
double fps
Definition types.h:308
const char * vidcap_params_get_driver(const struct vidcap_params *params)
const char * vidcap_params_get_fmt(const struct vidcap_params *params)
#define VIDEO_FRAME_DISPOSE(frame)
Definition video_frame.h:76

References vidcap_gpustitch_state::cam_properties, check_in_format(), cudaMalloc, vidcap_gpustitch_state::done, video_frame::fps, frame, grab_worker_state::grabbed_count, grab_worker_state::grabbed_cv, grab_worker_state::grabbed_fps, grab_worker_state::grabbed_mut, grab_worker_state::height, initialize_video_capture(), param, PROFILE_DETAIL, PROFILE_FUNC, RGBA, grab_worker_state::s, vidcap_gpustitch_state::stitched_count, vidcap_gpustitch_state::stitched_cv, vidcap_gpustitch_state::stitched_mut, grab_worker_state::tiled, grab_worker_state::tmp_in_frame_stream, grab_worker_state::tmp_rgba_frame, grab_worker_state::tmp_rgba_frame_pitch, upload_frame(), upload_tiles(), vc_get_linesize(), vidcap_done(), vidcap_grab(), vidcap_params_get_driver(), vidcap_params_get_fmt(), VIDEO_FRAME_DISPOSE, and grab_worker_state::width.

Referenced by vidcap_gpustitch_init().

Here is the call graph for this function:

◆ init_stitcher()

static bool init_stitcher ( struct vidcap_gpustitch_state * s)
static

Definition at line 389 of file gpustitch.cpp.

389 {
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}
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_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 log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
gpustitch::Stitcher_params stitch_params

References vidcap_gpustitch_state::cam_properties, vidcap_gpustitch_state::conv_func, cuda_RGBA_to_RGB(), cuda_RGBA_to_UYVY(), vidcap_gpustitch_state::devices_cnt, LOG_LEVEL_ERROR, log_msg(), vidcap_gpustitch_state::out_fmt, RGB, RGBA, vidcap_gpustitch_state::spec_path, vidcap_gpustitch_state::stitch_params, vidcap_gpustitch_state::stitcher, vidcap_gpustitch_state::tiled_capture, and UYVY.

Referenced by vidcap_gpustitch_init().

Here is the call graph for this function:

◆ parse_fmt()

static void parse_fmt ( vidcap_gpustitch_state * s,
const char *const fmt )
static

Definition at line 445 of file gpustitch.cpp.

445 {
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}
const char * fmt
codec_t get_codec_from_name(const char *name)
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
#define FMT_CMP(param)
const char * name
Definition settings.cpp:193

References fmt, FMT_CMP, vidcap_gpustitch_state::fps, get_codec_from_name(), LOG_LEVEL_WARNING, log_msg(), name, vidcap_gpustitch_state::out_fmt, vidcap_gpustitch_state::output_cuda_buf, RGBA, vidcap_gpustitch_state::spec_path, vidcap_gpustitch_state::stitch_params, and vidcap_gpustitch_state::tiled_capture.

Referenced by vidcap_gpustitch_init().

Here is the call graph for this function:

◆ REGISTER_MODULE()

REGISTER_MODULE ( gpustitch ,
& vidcap_gpustitch_info,
LIBRARY_CLASS_VIDEO_CAPTURE ,
VIDEO_CAPTURE_ABI_VERSION  )

◆ report_stats()

static void report_stats ( vidcap_gpustitch_state * s)
static

Definition at line 700 of file gpustitch.cpp.

700 {
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}
#define LOG_LEVEL_INFO
normal reporting
Definition debug.h:62
struct timeval t t0
double tv_diff(struct timeval curr_time, struct timeval prev_time)
Definition tv.c:97

References vidcap_gpustitch_state::fps, vidcap_gpustitch_state::frames, LOG_LEVEL_INFO, log_msg(), vidcap_gpustitch_state::t0, and tv_diff().

Referenced by vidcap_gpustitch_grab().

Here is the call graph for this function:

◆ result_data_delete()

static void result_data_delete ( struct video_frame * buf)
static

Definition at line 610 of file gpustitch.cpp.

610 {
611 if(!buf)
612 return;
613
614 cudaFreeHost(buf->tiles[0].data);
615}
#define cudaFreeHost

References cudaFreeHost, tile::data, and video_frame::tiles.

Referenced by allocate_result_frame().

◆ show_help()

static void show_help ( void )
static

Definition at line 80 of file gpustitch.cpp.

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}
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

References color_printf(), TERM_BOLD, TERM_FG_RED, TERM_FG_RESET, and TERM_RESET.

Referenced by vidcap_gpustitch_init().

Here is the call graph for this function:

◆ stitch()

static struct video_frame * stitch ( struct vidcap_gpustitch_state * s)
static

Definition at line 724 of file gpustitch.cpp.

724 {
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}
static bool download_stitched(vidcap_gpustitch_state *s, cudaStream_t out_stream)

References cudaSuccess, download_stitched(), vidcap_gpustitch_state::frame, log_str, PROFILE_DETAIL, PROFILE_FUNC, vidcap_gpustitch_state::stitched_count, vidcap_gpustitch_state::stitched_cv, vidcap_gpustitch_state::stitched_mut, and vidcap_gpustitch_state::stitcher.

Referenced by vidcap_gpustitch_grab().

Here is the call graph for this function:

◆ stop_grab_workers()

static void stop_grab_workers ( vidcap_gpustitch_state * s)
static

Definition at line 511 of file gpustitch.cpp.

511 {
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}
static void * worker(void *args)
Definition alsa.c:190
std::vector< grab_worker_state > capture_workers

References vidcap_gpustitch_state::capture_workers, vidcap_gpustitch_state::done, vidcap_gpustitch_state::stitched_cv, vidcap_gpustitch_state::stitched_mut, and worker().

Referenced by vidcap_gpustitch_done(), and vidcap_gpustitch_init().

Here is the call graph for this function:

◆ toRadian()

static double toRadian ( double deg)
static

Definition at line 76 of file gpustitch.cpp.

76 {
77 return (deg / 180) * 3.14159265;
78}

◆ upload_frame()

static bool upload_frame ( grab_worker_state * gs,
video_frame * in_frame,
int i )
static

Definition at line 260 of file gpustitch.cpp.

260 {
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}
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)

References grab_worker_state::height, grab_worker_state::s, vidcap_gpustitch_state::stitcher, grab_worker_state::tmp_rgba_frame, grab_worker_state::tmp_rgba_frame_pitch, upload_to_cuda_buf(), and grab_worker_state::width.

Referenced by grab_worker().

Here is the call graph for this function:

◆ upload_tiles()

static void upload_tiles ( grab_worker_state * gs,
video_frame * frame )
static

Definition at line 284 of file gpustitch.cpp.

284 {
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}

References frame, grab_worker_state::height, grab_worker_state::s, vidcap_gpustitch_state::stitcher, grab_worker_state::tmp_rgba_frame, grab_worker_state::tmp_rgba_frame_pitch, upload_to_cuda_buf(), and grab_worker_state::width.

Referenced by grab_worker().

Here is the call graph for this function:

◆ upload_to_cuda_buf()

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

Definition at line 213 of file gpustitch.cpp.

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}
#define cudaMemcpyHostToDevice
int get_pf_block_bytes(codec_t codec)
returns codec_info_t::block_size_bytes

References video_frame::color_spec, grab_worker_state::conv_func, cudaMemcpyHostToDevice, cudaSuccess, tile::data, get_pf_block_bytes(), RGBA, video_frame::tiles, grab_worker_state::tmp_in_frame, vc_get_linesize(), and tile::width.

Referenced by upload_frame(), and upload_tiles().

Here is the call graph for this function:

◆ vidcap_gpustitch_done()

static void vidcap_gpustitch_done ( void * state)
static

Definition at line 595 of file gpustitch.cpp.

596{
598
599 if(!s) return;
600
602
603 free(s->captured_frames);
604 vf_free(s->frame);
606
607 delete s;
608}
void ** state
void vf_free(struct video_frame *buf)
Frees video_frame structure.
static void stop_grab_workers(vidcap_gpustitch_state *s)
struct video_frame ** captured_frames

References vidcap_gpustitch_state::captured_frames, vidcap_gpustitch_state::conv_tmp_frame, cudaFree, vidcap_gpustitch_state::frame, state, stop_grab_workers(), and vf_free().

Here is the call graph for this function:

◆ vidcap_gpustitch_grab()

static struct video_frame * vidcap_gpustitch_grab ( void * state,
struct audio_frame ** audio )
static

Definition at line 760 of file gpustitch.cpp.

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}
static void report_stats(vidcap_gpustitch_state *s)
static double wait_for_frames(struct vidcap_gpustitch_state *s)
static struct video_frame * stitch(struct vidcap_gpustitch_state *s)

References vidcap_gpustitch_state::fps, video_frame::fps, vidcap_gpustitch_state::frames, PROFILE_FUNC, report_stats(), state, stitch(), and wait_for_frames().

Here is the call graph for this function:

◆ vidcap_gpustitch_init()

static int vidcap_gpustitch_init ( struct vidcap_params * params,
void ** state )
static

Definition at line 525 of file gpustitch.cpp.

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}
static void parse_fmt(vidcap_gpustitch_state *s, const char *const fmt)
static bool init_stitcher(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 VIDCAP_INIT_NOERR
state not initialized, other action performed (typically help)
#define VIDCAP_INIT_OK
initialization successful
#define VIDCAP_INIT_FAIL
error ocured
#define VIDCAP_INIT_AUDIO_NOT_SUPPORTED
card does not support audio
static struct @19 params[100]
struct vidcap_params * vidcap_params_get_next(const struct vidcap_params *curr)
unsigned int vidcap_params_get_flags(const struct vidcap_params *params)
#define VIDCAP_FLAG_AUDIO_ANY

References vidcap_gpustitch_state::capture_workers, vidcap_gpustitch_state::captured_frames, vidcap_gpustitch_state::devices_cnt, vidcap_gpustitch_state::frame, vidcap_gpustitch_state::frames, grab_worker(), init_stitcher(), LOG_LEVEL_ERROR, log_msg(), params, parse_fmt(), show_help(), state, stop_grab_workers(), vidcap_gpustitch_state::t0, vidcap_gpustitch_state::tiled_capture, VIDCAP_FLAG_AUDIO_ANY, VIDCAP_INIT_AUDIO_NOT_SUPPORTED, VIDCAP_INIT_FAIL, VIDCAP_INIT_NOERR, VIDCAP_INIT_OK, vidcap_params_get_driver(), vidcap_params_get_flags(), vidcap_params_get_fmt(), and vidcap_params_get_next().

Here is the call graph for this function:

◆ vidcap_gpustitch_probe()

static void vidcap_gpustitch_probe ( device_info ** available_cards,
int * count,
void(** deleter )(void *) )
static

Definition at line 160 of file gpustitch.cpp.

161{
162 *deleter = free;
163 *available_cards = nullptr;
164 *count = 0;
165 return;
166}
int void(** deleter)(void *))
int count
Definition hd-rum.c:44

References count, and deleter.

◆ wait_for_frames()

static double wait_for_frames ( struct vidcap_gpustitch_state * s)
static

Definition at line 711 of file gpustitch.cpp.

711 {
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}

References vidcap_gpustitch_state::capture_workers, vidcap_gpustitch_state::fps, PROFILE_FUNC, vidcap_gpustitch_state::stitched_count, and worker().

Referenced by vidcap_gpustitch_grab().

Here is the call graph for this function:

Variable Documentation

◆ log_str

const char* log_str = "[gpustitch] "
static

Definition at line 71 of file gpustitch.cpp.

Referenced by allocate_result_frame(), check_in_format(), download_stitched(), and stitch().

◆ vidcap_gpustitch_info

const struct video_capture_info vidcap_gpustitch_info
static
Initial value:
= {
}
static void vidcap_gpustitch_done(void *state)
static int vidcap_gpustitch_init(struct vidcap_params *params, void **state)
static void vidcap_gpustitch_probe(device_info **available_cards, int *count, void(**deleter)(void *))
static struct video_frame * vidcap_gpustitch_grab(void *state, struct audio_frame **audio)
#define VIDCAP_NO_GENERIC_FPS_INDICATOR

Definition at line 787 of file gpustitch.cpp.