From a050ff861d5061e5589bd946a53114e8812fea43 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 7 Sep 2020 15:34:54 +0200 Subject: [PATCH 01/74] File: use generic FPS indicator --- src/video_capture/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_capture/file.c b/src/video_capture/file.c index dfcbb980a..4e78705c1 100644 --- a/src/video_capture/file.c +++ b/src/video_capture/file.c @@ -630,7 +630,7 @@ static const struct video_capture_info vidcap_file_info = { vidcap_file_init, vidcap_file_done, vidcap_file_grab, - false + true }; REGISTER_MODULE(file, &vidcap_file_info, LIBRARY_CLASS_VIDEO_CAPTURE, VIDEO_CAPTURE_ABI_VERSION); From 27a31d6e38c3b91c64f4d490e8e602fb45a42a73 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Tue, 8 Sep 2020 15:30:43 +0200 Subject: [PATCH 02/74] Opus encode: use 2.5 ms frame duration Use 2.5 ms by default to reduce latency. Added option "opus-frame-duration" --- src/audio/codec/libavcodec.cpp | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/audio/codec/libavcodec.cpp b/src/audio/codec/libavcodec.cpp index f364060ab..fffa790f9 100644 --- a/src/audio/codec/libavcodec.cpp +++ b/src/audio/codec/libavcodec.cpp @@ -58,6 +58,7 @@ extern "C" { #include #include +#include #include #include #include @@ -69,6 +70,7 @@ extern "C" { #include "utils/resource_manager.h" #define MAGIC 0xb135ca11 +#define DEFAULT_OPUS_FRAME_DURATION 2.5 #if LIBAVCODEC_VERSION_MAJOR < 54 #define AV_CODEC_ID_AAC CODEC_ID_AAC @@ -134,6 +136,10 @@ struct libavcodec_codec_state { }; static_assert(is_aggregate_v, "ensure aggregate to allow aggregate initialization"); +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +ADD_TO_PARAM("opus-frame-duration", "* opus-frame-duration=\n" + " Sets OPUS frame duration, default is " STR(DEFAULT_OPUS_FRAME_DURATION) " ms\n"); /** * Initializates selected audio codec * @param audio_codec requested audio codec @@ -185,11 +191,11 @@ static void *libavcodec_init(audio_codec_t audio_codec, audio_codec_direction_t } delete s; return NULL; - } else { - if (!silent) { - LOG(LOG_LEVEL_NOTICE) << MOD_NAME << "Using audio " << - (direction == AUDIO_CODER ? "en"s : "de"s) << "coder: " << s->codec->name << "\n"; - } + } + + if (!silent) { + LOG(LOG_LEVEL_NOTICE) << MOD_NAME << "Using audio " << + (direction == AUDIO_CODER ? "en"s : "de"s) << "coder: " << s->codec->name << "\n"; } s->libav_global_lock = rm_acquire_shared_lock(LAVCD_LOCK_NAME); @@ -204,6 +210,19 @@ static void *libavcodec_init(audio_codec_t audio_codec, audio_codec_direction_t s->codec_ctx->strict_std_compliance = -2; + if (direction == AUDIO_CODER && s->codec->id == AV_CODEC_ID_OPUS) { + double frame_duration = commandline_params.find("opus-frame-duration"s) == commandline_params.end() ? + DEFAULT_OPUS_FRAME_DURATION : stof(commandline_params.at("opus-frame-duration"s), nullptr); + string frame_duration_str{to_string(frame_duration)}; + int ret = av_opt_set(s->codec_ctx->priv_data, "frame_duration", frame_duration_str.c_str(), 0); + if (ret != 0) { + array errbuf{}; + av_strerror(ret, errbuf.data(), errbuf.size()); + LOG(LOG_LEVEL_WARNING) << MOD_NAME << "Could set OPUS frame duration: " + << errbuf.data() << " (" << ret << ")\n"; + } + } + s->bitrate = bitrate; s->av_frame = av_frame_alloc(); From db7cc0e66310c438dfd4f406887a011bf5c9ac63 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Tue, 8 Sep 2020 16:40:34 +0200 Subject: [PATCH 03/74] Audio frame size adjustable also for OPUS Do not set the frame size by default (causes underruns for OPUS). --- src/audio/codec/libavcodec.cpp | 43 +++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/src/audio/codec/libavcodec.cpp b/src/audio/codec/libavcodec.cpp index fffa790f9..cc074f571 100644 --- a/src/audio/codec/libavcodec.cpp +++ b/src/audio/codec/libavcodec.cpp @@ -70,7 +70,7 @@ extern "C" { #include "utils/resource_manager.h" #define MAGIC 0xb135ca11 -#define DEFAULT_OPUS_FRAME_DURATION 2.5 +#define LOW_LATENCY_AUDIOENC_FRAME_DURATION 2.5 #if LIBAVCODEC_VERSION_MAJOR < 54 #define AV_CODEC_ID_AAC CODEC_ID_AAC @@ -138,8 +138,8 @@ static_assert(is_aggregate_v, "ensure aggregate to allow #define STR_HELPER(x) #x #define STR(x) STR_HELPER(x) -ADD_TO_PARAM("opus-frame-duration", "* opus-frame-duration=\n" - " Sets OPUS frame duration, default is " STR(DEFAULT_OPUS_FRAME_DURATION) " ms\n"); +ADD_TO_PARAM("audioenc-frame-duration", "* audioenc-frame-duration=\n" + " Sets audio encoder frame duration (in ms), default is " STR(LOW_LATENCY_OPUS_FRAME_DURATION) " ms for low-latency-audio\n"); /** * Initializates selected audio codec * @param audio_codec requested audio codec @@ -152,6 +152,10 @@ ADD_TO_PARAM("opus-frame-duration", "* opus-frame-duration=\n" static void *libavcodec_init(audio_codec_t audio_codec, audio_codec_direction_t direction, bool silent, int bitrate) { + if (log_level >= LOG_LEVEL_VERBOSE) { + av_log_set_level(AV_LOG_VERBOSE); + } + enum AVCodecID codec_id = AV_CODEC_ID_NONE; auto it = mapping.find(audio_codec); @@ -210,19 +214,6 @@ static void *libavcodec_init(audio_codec_t audio_codec, audio_codec_direction_t s->codec_ctx->strict_std_compliance = -2; - if (direction == AUDIO_CODER && s->codec->id == AV_CODEC_ID_OPUS) { - double frame_duration = commandline_params.find("opus-frame-duration"s) == commandline_params.end() ? - DEFAULT_OPUS_FRAME_DURATION : stof(commandline_params.at("opus-frame-duration"s), nullptr); - string frame_duration_str{to_string(frame_duration)}; - int ret = av_opt_set(s->codec_ctx->priv_data, "frame_duration", frame_duration_str.c_str(), 0); - if (ret != 0) { - array errbuf{}; - av_strerror(ret, errbuf.data(), errbuf.size()); - LOG(LOG_LEVEL_WARNING) << MOD_NAME << "Could set OPUS frame duration: " - << errbuf.data() << " (" << ret << ")\n"; - } - } - s->bitrate = bitrate; s->av_frame = av_frame_alloc(); @@ -317,6 +308,7 @@ static bool reinitialize_coder(struct libavcodec_codec_state *s, struct audio_de s->codec_ctx->channel_layout = AV_CH_LAYOUT_MONO; #endif + pthread_mutex_lock(s->libav_global_lock); /* open it */ if (avcodec_open2(s->codec_ctx, s->codec, NULL) < 0) { @@ -326,6 +318,25 @@ static bool reinitialize_coder(struct libavcodec_codec_state *s, struct audio_de } pthread_mutex_unlock(s->libav_global_lock); + if (s->direction == AUDIO_CODER && (commandline_params.find("low-latency-audio"s) != commandline_params.end() + || commandline_params.find("audioenc-frame-duration"s) != commandline_params.end())) { + double frame_duration = commandline_params.find("audioenc-frame-duration"s) == commandline_params.end() ? + LOW_LATENCY_AUDIOENC_FRAME_DURATION : stof(commandline_params.at("audioenc-frame-duration"s), nullptr); + if (s->codec->id == AV_CODEC_ID_OPUS) { + string frame_duration_str{to_string(frame_duration)}; + int ret = av_opt_set(s->codec_ctx->priv_data, "frame_duration", frame_duration_str.c_str(), 0); + if (ret != 0) { + array errbuf{}; + av_strerror(ret, errbuf.data(), errbuf.size()); + LOG(LOG_LEVEL_WARNING) << MOD_NAME << "Could set OPUS frame duration: " + << errbuf.data() << " (" << ret << ")\n"; + } + } + if (s->codec->id == AV_CODEC_ID_FLAC) { + s->codec_ctx->frame_size = desc.sample_rate * frame_duration / std::chrono::milliseconds::period::den; + } + } + if(s->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE) { s->codec_ctx->frame_size = 1; } From 81bed146035c882ac6756fb5da010ee17afbcd95 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Tue, 8 Sep 2020 16:56:54 +0200 Subject: [PATCH 04/74] Added low-latency-audio=ultra --- src/audio/audio.cpp | 2 +- src/host.cpp | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/audio/audio.cpp b/src/audio/audio.cpp index 45b4bfed1..ffa6887b5 100644 --- a/src/audio/audio.cpp +++ b/src/audio/audio.cpp @@ -683,7 +683,7 @@ static void *audio_receiver_thread(void *arg) dec_state = (struct audio_decoder *) calloc(1, sizeof(struct audio_decoder)); if (get_commandline_param("low-latency-audio")) { - pbuf_set_playout_delay(cp->playout_buffer, 0.005); + pbuf_set_playout_delay(cp->playout_buffer, strcmp(get_commandline_param("low-latency-audio"), "ultra") == 0 ? 0.001 :0.005); } assert(dec_state != NULL); cp->decoder_state = dec_state; diff --git a/src/host.cpp b/src/host.cpp index 0b4e9f124..2fe86905d 100644 --- a/src/host.cpp +++ b/src/host.cpp @@ -541,8 +541,9 @@ ADD_TO_PARAM("audio-cap-frames", "* audio-cap-frames=\n" " Sets number of audio frames captured at once (CoreAudio)\n"); ADD_TO_PARAM("audio-disable-adaptive-buffer", "* audio-disable-adaptive-buffer\n" " Disables audio adaptive playback buffer (CoreAudio/JACK)\n"); -ADD_TO_PARAM("low-latency-audio", "* low-latency-audio\n" - " Try to reduce audio latency at the expense of worse reliability\n"); +ADD_TO_PARAM("low-latency-audio", "* low-latency-audio[=ultra]\n" + " Try to reduce audio latency at the expense of worse reliability\n" + " Add ultra for even more aggressive setting.\n"); ADD_TO_PARAM("window-title", "* window-title=\n" " Use alternative window title (SDL/GL only)\n"); From f4ac558bcf94926de5827857d2ca621e0c21e4c4 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Wed, 9 Sep 2020 09:10:57 +0200 Subject: [PATCH 05/74] Lavc audio: frame len needs to be set before open --- src/audio/codec/libavcodec.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/audio/codec/libavcodec.cpp b/src/audio/codec/libavcodec.cpp index cc074f571..7ae5fc379 100644 --- a/src/audio/codec/libavcodec.cpp +++ b/src/audio/codec/libavcodec.cpp @@ -308,16 +308,6 @@ static bool reinitialize_coder(struct libavcodec_codec_state *s, struct audio_de s->codec_ctx->channel_layout = AV_CH_LAYOUT_MONO; #endif - - pthread_mutex_lock(s->libav_global_lock); - /* open it */ - if (avcodec_open2(s->codec_ctx, s->codec, NULL) < 0) { - fprintf(stderr, "Could not open codec\n"); - pthread_mutex_unlock(s->libav_global_lock); - return false; - } - pthread_mutex_unlock(s->libav_global_lock); - if (s->direction == AUDIO_CODER && (commandline_params.find("low-latency-audio"s) != commandline_params.end() || commandline_params.find("audioenc-frame-duration"s) != commandline_params.end())) { double frame_duration = commandline_params.find("audioenc-frame-duration"s) == commandline_params.end() ? @@ -337,6 +327,15 @@ static bool reinitialize_coder(struct libavcodec_codec_state *s, struct audio_de } } + pthread_mutex_lock(s->libav_global_lock); + /* open it */ + if (avcodec_open2(s->codec_ctx, s->codec, NULL) < 0) { + fprintf(stderr, "Could not open codec\n"); + pthread_mutex_unlock(s->libav_global_lock); + return false; + } + pthread_mutex_unlock(s->libav_global_lock); + if(s->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE) { s->codec_ctx->frame_size = 1; } From 8ea7ab09b599ad39da5e541c98bfbcac7227185e Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Wed, 9 Sep 2020 09:13:20 +0200 Subject: [PATCH 06/74] Lavc aud.: print error when open fails --- src/audio/codec/libavcodec.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/audio/codec/libavcodec.cpp b/src/audio/codec/libavcodec.cpp index 7ae5fc379..6dc4a4e54 100644 --- a/src/audio/codec/libavcodec.cpp +++ b/src/audio/codec/libavcodec.cpp @@ -329,8 +329,10 @@ static bool reinitialize_coder(struct libavcodec_codec_state *s, struct audio_de pthread_mutex_lock(s->libav_global_lock); /* open it */ - if (avcodec_open2(s->codec_ctx, s->codec, NULL) < 0) { - fprintf(stderr, "Could not open codec\n"); + if (int ret = 0; (ret = avcodec_open2(s->codec_ctx, s->codec, nullptr)) < 0) { + array<char, ERR_MSG_BUF_LEN> errbuf{}; + av_strerror(ret, errbuf.data(), errbuf.size()); + LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Could not open codec: " << errbuf.data() << "(" << ret << ")\n"; pthread_mutex_unlock(s->libav_global_lock); return false; } From 87bd2cf368a9a070a44e9ae9a53802a386fdafc2 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Wed, 9 Sep 2020 09:47:27 +0200 Subject: [PATCH 07/74] Lavc audio: realloc codec context Codec context needs to be realloced because avcodec_close() strips codec specific part from the context and then eg. setting frame_duration for OPUS fails. --- src/audio/codec/libavcodec.cpp | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/audio/codec/libavcodec.cpp b/src/audio/codec/libavcodec.cpp index 6dc4a4e54..5caff9ce1 100644 --- a/src/audio/codec/libavcodec.cpp +++ b/src/audio/codec/libavcodec.cpp @@ -211,7 +211,6 @@ static void *libavcodec_init(audio_codec_t audio_codec, audio_codec_direction_t delete s; return NULL; } - s->codec_ctx->strict_std_compliance = -2; s->bitrate = bitrate; @@ -247,9 +246,12 @@ static bool reinitialize_coder(struct libavcodec_codec_state *s, struct audio_de { cleanup_common(s); - pthread_mutex_lock(s->libav_global_lock); - avcodec_close(s->codec_ctx); - pthread_mutex_unlock(s->libav_global_lock); + s->codec_ctx = avcodec_alloc_context3(s->codec); + if (s->codec_ctx == nullptr) { // not likely :) + LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Could not allocate audio codec context\n"; + return false; + } + s->codec_ctx->strict_std_compliance = -2; /* put sample parameters */ if (s->bitrate > 0) { @@ -371,9 +373,12 @@ static bool reinitialize_decoder(struct libavcodec_codec_state *s, struct audio_ { cleanup_common(s); - pthread_mutex_lock(s->libav_global_lock); - avcodec_close(s->codec_ctx); - pthread_mutex_unlock(s->libav_global_lock); + s->codec_ctx = avcodec_alloc_context3(s->codec); + if (s->codec_ctx == nullptr) { // not likely :) + LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Could not allocate audio codec context\n"; + return false; + } + s->codec_ctx->strict_std_compliance = -2; s->codec_ctx->channels = 1; @@ -667,6 +672,11 @@ static void cleanup_common(struct libavcodec_codec_state *s) #endif } + pthread_mutex_lock(s->libav_global_lock); + avcodec_close(s->codec_ctx); + avcodec_free_context(&s->codec_ctx); + pthread_mutex_unlock(s->libav_global_lock); + s->context_initialized = false; } @@ -677,11 +687,6 @@ static void libavcodec_done(void *state) cleanup_common(s); - pthread_mutex_lock(s->libav_global_lock); - avcodec_close(s->codec_ctx); - avcodec_free_context(&s->codec_ctx); - pthread_mutex_unlock(s->libav_global_lock); - rm_release_shared_lock(LAVCD_LOCK_NAME); av_frame_free(&s->av_frame); From 89747981dae56d03b75a39f311f086fd070f33d8 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Wed, 9 Sep 2020 14:27:26 +0200 Subject: [PATCH 08/74] audio_capture_channels is now 0 for default Capturers may decide by themselves or capture DEFAULT_AUDIO_CAPTURE_CHANNELS (as until now). This fixes default behavior of WASAPI with Realtek sound cards that is unable to capture single channel. --- src/audio/capture/alsa.c | 6 +++--- src/audio/capture/coreaudio.m | 2 +- src/audio/capture/jack.c | 6 +++--- src/audio/capture/portaudio.c | 8 +++----- src/audio/capture/testcard.cpp | 2 +- src/audio/capture/wasapi.cpp | 7 +++---- src/host.cpp | 2 +- src/host.h | 3 ++- src/video_capture/aja.cpp | 6 +++--- src/video_capture/bluefish444.cpp | 4 ++-- src/video_capture/decklink.cpp | 28 ++++++++++++++-------------- src/video_capture/deltacast.cpp | 13 +++++-------- src/video_capture/dvs.c | 8 ++++---- src/video_capture/ndi.cpp | 19 ++++++++++++++----- src/video_capture/testcard.cpp | 18 +++++++++--------- src/video_capture/testcard2.c | 15 ++++++--------- 16 files changed, 74 insertions(+), 73 deletions(-) diff --git a/src/audio/capture/alsa.c b/src/audio/capture/alsa.c index 2f200f649..09107dd3b 100644 --- a/src/audio/capture/alsa.c +++ b/src/audio/capture/alsa.c @@ -146,7 +146,7 @@ static void * audio_cap_alsa_init(const char *cfg) gettimeofday(&s->start_time, NULL); s->frame.bps = audio_capture_bps; s->frame.sample_rate = audio_capture_sample_rate; - s->min_device_channels = s->frame.ch_count = audio_capture_channels; + s->min_device_channels = s->frame.ch_count = audio_capture_channels > 0 ? audio_capture_channels : DEFAULT_AUDIO_CAPTURE_CHANNELS; s->tmp_data = NULL; /* Set period size to 128 frames or more. */ @@ -218,7 +218,7 @@ static void * audio_cap_alsa_init(const char *cfg) if (!snd_pcm_hw_params_test_access(s->handle, params, SND_PCM_ACCESS_RW_INTERLEAVED)) { s->non_interleaved = false; } else if (!snd_pcm_hw_params_test_access(s->handle, params, SND_PCM_ACCESS_RW_NONINTERLEAVED)) { - if (audio_capture_channels > 1) { + if (s->frame.ch_count > 1) { log_msg(LOG_LEVEL_ERROR, MOD_NAME "Non-interleaved mode " "available only when capturing mono!\n"); goto error; @@ -339,7 +339,7 @@ static struct audio_frame *audio_cap_alsa_read(void *state) } if (s->non_interleaved) { - assert(audio_capture_channels == 1); + assert(s->frame.ch_count == 1); discard_data = (char *) alloca(s->frames * s->frame.bps * (s->min_device_channels-1)); for (unsigned int i = 1; i < s->min_device_channels; ++i) { read_ptr[i] = discard_data + (i - 1) * s->frames * s->frame.bps; diff --git a/src/audio/capture/coreaudio.m b/src/audio/capture/coreaudio.m index 05bafeec0..b26a53785 100644 --- a/src/audio/capture/coreaudio.m +++ b/src/audio/capture/coreaudio.m @@ -276,7 +276,7 @@ static void * audio_cap_ca_init(const char *cfg) s->boss_waiting = FALSE; s->data_ready = FALSE; s->frame.bps = audio_capture_bps ? audio_capture_bps : 2; - s->frame.ch_count = audio_capture_channels; + s->frame.ch_count = audio_capture_channels > 0 ? audio_capture_channels : DEFAULT_AUDIO_CAPTURE_CHANNELS; double rate; size = sizeof(double); diff --git a/src/audio/capture/jack.c b/src/audio/capture/jack.c index c2801d729..c2d2c5d7e 100644 --- a/src/audio/capture/jack.c +++ b/src/audio/capture/jack.c @@ -206,14 +206,14 @@ static void * audio_cap_jack_init(const char *cfg) i = 0; while(ports[i]) i++; - if(i < (int) audio_capture_channels) { + s->frame.ch_count = audio_capture_channels > 0 ? audio_capture_channels : DEFAULT_AUDIO_CAPTURE_CHANNELS; + if (i < s->frame.ch_count) { log_msg(LOG_LEVEL_ERROR, MOD_NAME "Requested channel count %d not found (matching pattern %s).\n", - audio_capture_channels, cfg); + s->frame.ch_count, cfg); goto release_client; } - s->frame.ch_count = audio_capture_channels; s->frame.bps = 4; if (audio_capture_sample_rate) { log_msg(LOG_LEVEL_WARNING, "[JACK capture] Ignoring user specified sample rate!\n"); diff --git a/src/audio/capture/portaudio.c b/src/audio/capture/portaudio.c index 02039d28f..6b74f3118 100644 --- a/src/audio/capture/portaudio.c +++ b/src/audio/capture/portaudio.c @@ -219,11 +219,10 @@ static void * audio_cap_portaudio_init(const char *cfg) return NULL; } - if((int) audio_capture_channels <= device_info->maxInputChannels) { - inputParameters.channelCount = audio_capture_channels; - } else { + inputParameters.channelCount = s->frame.ch_count = audio_capture_channels > 0 ? audio_capture_channels : MAX(device_info->maxInputChannels, DEFAULT_AUDIO_CAPTURE_CHANNELS); + if (s->frame.ch_count > device_info->maxInputChannels) { fprintf(stderr, MODULE_NAME "Requested %d input channels, device offers only %d.\n", - audio_capture_channels, + s->frame.ch_count, device_info->maxInputChannels); free(s); return NULL; @@ -258,7 +257,6 @@ static void * audio_cap_portaudio_init(const char *cfg) return NULL; } - s->frame.ch_count = inputParameters.channelCount; s->frame.max_size = (s->frame.bps * s->frame.ch_count) * s->frame.sample_rate / 1000 * BUF_MS; s->frame.data = (char*)malloc(s->frame.max_size); diff --git a/src/audio/capture/testcard.cpp b/src/audio/capture/testcard.cpp index 005175336..ffc0223ae 100644 --- a/src/audio/capture/testcard.cpp +++ b/src/audio/capture/testcard.cpp @@ -220,7 +220,7 @@ static void * audio_cap_testcard_init(const char *cfg) case EBU: case SILENCE: { - s->audio.ch_count = audio_capture_channels; + s->audio.ch_count = audio_capture_channels > 0 ? audio_capture_channels : DEFAULT_AUDIO_CAPTURE_CHANNELS; s->audio.sample_rate = audio_capture_sample_rate ? audio_capture_sample_rate : DEFAULT_AUDIO_SAMPLE_RATE; s->audio.bps = audio_capture_bps ? audio_capture_bps : DEFAULT_AUDIO_BPS; diff --git a/src/audio/capture/wasapi.cpp b/src/audio/capture/wasapi.cpp index 7bd88a9ab..2a1a22b2e 100644 --- a/src/audio/capture/wasapi.cpp +++ b/src/audio/capture/wasapi.cpp @@ -265,7 +265,7 @@ static void * audio_cap_wasapi_init(const char *cfg) // get the mixer format THROW_IF_FAILED(s->pAudioClient->GetMixFormat(&pwfx)); // set our preferences - if (audio_capture_channels) { + if (audio_capture_channels != 0) { pwfx->nChannels = audio_capture_channels; } if (audio_capture_sample_rate) { @@ -327,9 +327,8 @@ static void * audio_cap_wasapi_init(const char *cfg) } catch (ug_runtime_error &e) { LOG(LOG_LEVEL_ERROR) << MOD_NAME << e.what() << "\n"; - if (audio_capture_channels != DEFAULT_AUDIO_CAPTURE_CHANNELS) { - LOG(LOG_LEVEL_INFO) << MOD_NAME << "Maybe wrong number of channels? Default: " - << DEFAULT_AUDIO_CAPTURE_CHANNELS << ", requested: " << audio_capture_channels << "\n"; + if (audio_capture_channels != 0) { + LOG(LOG_LEVEL_WARNING) << MOD_NAME << "Maybe wrong number of channels? Try using default."; } CoUninitialize(); delete s; diff --git a/src/host.cpp b/src/host.cpp index 2fe86905d..f5953dd13 100644 --- a/src/host.cpp +++ b/src/host.cpp @@ -84,7 +84,7 @@ using rang::style; using namespace std; -unsigned int audio_capture_channels = DEFAULT_AUDIO_CAPTURE_CHANNELS; +unsigned int audio_capture_channels = 0; unsigned int audio_capture_bps = 0; unsigned int audio_capture_sample_rate = 0; diff --git a/src/host.h b/src/host.h index 1b8b5eb27..980c7e5fd 100644 --- a/src/host.h +++ b/src/host.h @@ -83,7 +83,8 @@ void error(int status); void exit_uv(int status); #define DEFAULT_AUDIO_CAPTURE_CHANNELS 1 -extern unsigned int audio_capture_channels; +extern unsigned int audio_capture_channels; ///< user-specified chan. count, if zero, module should choose + ///< best/native or DEFAULT_AUDIO_CAPTURE_CHANNELS extern unsigned int audio_capture_bps; // user-specified bps, if zero, module should choose // best bps by itself extern unsigned int audio_capture_sample_rate; // user-specified sample rate, if zero, module should diff --git a/src/video_capture/aja.cpp b/src/video_capture/aja.cpp index ddffbb44b..ac6bff5e0 100644 --- a/src/video_capture/aja.cpp +++ b/src/video_capture/aja.cpp @@ -713,9 +713,10 @@ AJAStatus vidcap_state_aja::SetupAudio (void) #endif mMaxAudioChannels = ::NTV2DeviceGetMaxAudioChannels (mDeviceID); - if (mMaxAudioChannels < (int) *aja_audio_capture_channels) { + mAudio.ch_count = *aja_audio_capture_channels > 0 ? *aja_audio_capture_channels : DEFAULT_AUDIO_CAPTURE_CHANNELS; + if (mMaxAudioChannels < mAudio.ch_count) { LOG(LOG_LEVEL_ERROR) << MOD_NAME "Invalid number of capture channels requested. Requested " << - *aja_audio_capture_channels << ", maximum " << mMaxAudioChannels << endl; + mAudio.ch_count << ", maximum " << mMaxAudioChannels << endl; return AJA_STATUS_FAIL; } if (!mDevice.SetNumberAudioChannels (mMaxAudioChannels, NTV2InputSourceToAudioSystem(mInputSource))) { @@ -747,7 +748,6 @@ AJAStatus vidcap_state_aja::SetupAudio (void) mAudio.bps = 4; mAudio.sample_rate = 48000; mAudio.data = (char *) malloc(NTV2_AUDIOSIZE_MAX); - mAudio.ch_count = *aja_audio_capture_channels; mAudio.max_size = NTV2_AUDIOSIZE_MAX; #ifndef _MSC_VER diff --git a/src/video_capture/bluefish444.cpp b/src/video_capture/bluefish444.cpp index eecd0aa49..8247a83c7 100644 --- a/src/video_capture/bluefish444.cpp +++ b/src/video_capture/bluefish444.cpp @@ -377,9 +377,10 @@ static bool setup_audio(struct vidcap_bluefish444_state *s, unsigned int flags) { memset(&s->objHancDecode, 0, sizeof(s->objHancDecode)); + s->audio.ch_count = audio_capture_channels > 0 ? audio_capture_channels : DEFAULT_AUDIO_CAPTURE_CHANNELS; s->objHancDecode.audio_ch_required_mask = 0; /* MONO_CHANNEL_9 and _10 are used for analog output */ - switch(audio_capture_channels) { + switch (s->audio.ch_count) { case 16: s->objHancDecode.audio_ch_required_mask |= MONO_CHANNEL_18; // fall through @@ -444,7 +445,6 @@ static bool setup_audio(struct vidcap_bluefish444_state *s, unsigned int flags) } s->audio.bps = 2; - s->audio.ch_count = audio_capture_channels; s->audio.sample_rate = 48000; // perhaps the driver does not support different s->audio.max_size = 4*4096*16; diff --git a/src/video_capture/decklink.cpp b/src/video_capture/decklink.cpp index 1b12dcdc5..2c60b72e8 100644 --- a/src/video_capture/decklink.cpp +++ b/src/video_capture/decklink.cpp @@ -9,7 +9,7 @@ * Dalibor Matura <255899@mail.muni.cz> * Ian Wesley-Smith <iwsmith@cct.lsu.edu> * - * Copyright (c) 2005-2019 CESNET z.s.p.o. + * Copyright (c) 2005-2020 CESNET z.s.p.o. * * Redistribution and use in source and binary forms, with or without * modification, is permitted provided that the following conditions @@ -52,6 +52,7 @@ #include "config_unix.h" #include "config_win32.h" +#include <algorithm> #include <cassert> #include <condition_variable> #include <chrono> @@ -63,7 +64,6 @@ #include <set> #include <string> #include <vector> -#include <algorithm> #include "blackmagic_common.h" #include "audio/audio.h" @@ -302,7 +302,7 @@ public: bmdAudioSampleRate48kHz, s->audio.bps == 2 ? bmdAudioSampleType16bitInteger : bmdAudioSampleType32bitInteger, - audio_capture_channels == 1 ? 2 : audio_capture_channels); // BMD isn't able to grab single channel + max(s->audio.ch_count, 2)); // BMD isn't able to grab single channel } //deckLinkInput->SetCallback(s->state[i].delegate); deckLinkInput->FlushStreams(); @@ -1085,7 +1085,7 @@ vidcap_decklink_init(struct vidcap_params *params, void **state) log_msg(LOG_LEVEL_WARNING, "[Decklink] Ignoring unsupported sample rate!\n"); } s->audio.sample_rate = 48000; - s->audio.ch_count = audio_capture_channels; + s->audio.ch_count = audio_capture_channels > 0 ? audio_capture_channels : DEFAULT_AUDIO_CAPTURE_CHANNELS; s->audio.max_size = (s->audio.sample_rate / 10) * s->audio.ch_count * s->audio.bps; s->audio.data = (char *) malloc(s->audio.max_size); } else { @@ -1354,12 +1354,12 @@ vidcap_decklink_init(struct vidcap_params *params, void **state) fprintf(stderr, "[Decklink capture] Unable to set audio input!!! Please check if it is OK. Continuing anyway.\n"); } - if (audio_capture_channels != 1 && - audio_capture_channels != 2 && - audio_capture_channels != 8 && - audio_capture_channels != 16) { + if (s->audio.ch_count != 1 && + s->audio.ch_count != 2 && + s->audio.ch_count != 8 && + s->audio.ch_count != 16) { fprintf(stderr, "[DeckLink] Decklink cannot grab %d audio channels. " - "Only 1, 2, 8 or 16 are possible.", audio_capture_channels); + "Only 1, 2, 8 or 16 are possible.", s->audio.ch_count); goto error; } if (s->audio_consumer_levels != -1) { @@ -1372,7 +1372,7 @@ vidcap_decklink_init(struct vidcap_params *params, void **state) CALL_AND_CHECK(deckLinkInput->EnableAudioInput( bmdAudioSampleRate48kHz, s->audio.bps == 2 ? bmdAudioSampleType16bitInteger : bmdAudioSampleType32bitInteger, - audio_capture_channels == 1 ? 2 : audio_capture_channels), + max(s->audio.ch_count, 2)), // capture at least 2 "EnableAudioInput", "Decklink audio capture initialized sucessfully: " << audio_desc_from_frame(&s->audio)); } @@ -1541,7 +1541,7 @@ static audio_frame *process_new_audio_packets(struct vidcap_decklink_state *s) { void *audioFrame = nullptr; audioPacket->GetBytes(&audioFrame); - if(audio_capture_channels == 1) { // there are actually 2 channels grabbed + if (s->audio.ch_count == 1) { // there are actually 2 channels grabbed if (s->audio.data_len + audioPacket->GetSampleFrameCount() * 1U * s->audio.bps <= static_cast<unsigned>(s->audio.max_size)) { demux_channel(s->audio.data + s->audio.data_len, static_cast<char *>(audioFrame), s->audio.bps, min<int64_t>(audioPacket->GetSampleFrameCount() * 2 /* channels */ * s->audio.bps, INT_MAX), 2 /* channels (originally) */, 0 /* we want first channel */); s->audio.data_len = min<int64_t>(s->audio.data_len + audioPacket->GetSampleFrameCount() * 1 * s->audio.bps, INT_MAX); @@ -1549,9 +1549,9 @@ static audio_frame *process_new_audio_packets(struct vidcap_decklink_state *s) { LOG(LOG_LEVEL_WARNING) << "[DeckLink] Audio frame too small!\n"; } } else { - if (s->audio.data_len + audioPacket->GetSampleFrameCount() * audio_capture_channels * s->audio.bps <= static_cast<unsigned>(s->audio.max_size)) { - memcpy(s->audio.data + s->audio.data_len, audioFrame, audioPacket->GetSampleFrameCount() * audio_capture_channels * s->audio.bps); - s->audio.data_len = min<int64_t>(s->audio.data_len + audioPacket->GetSampleFrameCount() * audio_capture_channels * s->audio.bps, INT_MAX); + if (s->audio.data_len + audioPacket->GetSampleFrameCount() * s->audio.ch_count * s->audio.bps <= static_cast<unsigned>(s->audio.max_size)) { + memcpy(s->audio.data + s->audio.data_len, audioFrame, audioPacket->GetSampleFrameCount() * s->audio.ch_count * s->audio.bps); + s->audio.data_len = min<int64_t>(s->audio.data_len + audioPacket->GetSampleFrameCount() * s->audio.ch_count * s->audio.bps, INT_MAX); } else { LOG(LOG_LEVEL_WARNING) << "[DeckLink] Audio frame too small!\n"; } diff --git a/src/video_capture/deltacast.cpp b/src/video_capture/deltacast.cpp index 1fe8c40b7..22c96c552 100644 --- a/src/video_capture/deltacast.cpp +++ b/src/video_capture/deltacast.cpp @@ -52,6 +52,7 @@ #include "audio/utils.h" #include "deltacast_common.hpp" +#include <algorithm> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> @@ -279,21 +280,17 @@ static bool wait_for_channel(struct vidcap_deltacast_state *s) } if ((s->initialize_flags & VIDCAP_FLAG_AUDIO_EMBEDDED) == 0u) { - if(audio_capture_channels != 1 && - audio_capture_channels != 2) { + s->audio_frame.ch_count = audio_capture_channels > 0 ? audio_capture_channels : max(DEFAULT_AUDIO_CAPTURE_CHANNELS, 2); + if (s->audio_frame.ch_count != 1 && + s->audio_frame.ch_count != 2) { log_msg(LOG_LEVEL_ERROR, "[DELTACAST capture] Unable to handle channel count other than 1 or 2.\n"); throw delta_init_exception(); } s->audio_frame.bps = 3; s->audio_frame.sample_rate = 48000; - s->audio_frame.ch_count = audio_capture_channels; memset(&s->AudioInfo, 0, sizeof(VHD_AUDIOINFO)); s->pAudioChn = &s->AudioInfo.pAudioGroups[0].pAudioChannels[0]; - if(audio_capture_channels == 1) { - s->pAudioChn->Mode = s->AudioInfo.pAudioGroups[0].pAudioChannels[1].Mode=VHD_AM_MONO; - } else if(audio_capture_channels == 2) { - s->pAudioChn->Mode = s->AudioInfo.pAudioGroups[0].pAudioChannels[1].Mode=VHD_AM_STEREO; - } else abort(); + s->pAudioChn->Mode = s->AudioInfo.pAudioGroups[0].pAudioChannels[1].Mode= s->audio_frame.ch_count == 1 ? VHD_AM_MONO : VHD_AM_STEREO; s->pAudioChn->BufferFormat = s->AudioInfo.pAudioGroups[0].pAudioChannels[1].BufferFormat=VHD_AF_24; /* Get the biggest audio frame size */ diff --git a/src/video_capture/dvs.c b/src/video_capture/dvs.c index 86fd46da3..475cebb3e 100644 --- a/src/video_capture/dvs.c +++ b/src/video_capture/dvs.c @@ -482,17 +482,17 @@ static int vidcap_dvs_init(struct vidcap_params *params, void **state) if (res != SV_OK) { goto error; } - if(audio_capture_channels != 2 && audio_capture_channels != 1) { + s->audio.ch_count = audio_capture_channels > 0 ? audio_capture_channels : DEFAULT_AUDIO_CAPTURE_CHANNELS; + if (s->audio.ch_count != 2 && s->audio.ch_count != 1) { fprintf(stderr, "[DVS cap.] Invalid channel count %d. " "Currently only 1 or 2 channels are supported.\n", - audio_capture_channels); + s->audio.ch_count); goto error; } res = sv_option(s->sv, SV_OPTION_AUDIOCHANNELS, 1); // one pair if (res != SV_OK) { goto error; } - s->audio.ch_count = audio_capture_channels; sv_query(s->sv, SV_QUERY_AUDIOBITS, 0, &i); s->audio.bps = i / 8; @@ -504,7 +504,7 @@ static int vidcap_dvs_init(struct vidcap_params *params, void **state) s->audio_bufs[0] = malloc(s->audio.sample_rate * 2 * s->audio.bps); s->audio_bufs[1] = malloc(s->audio.sample_rate * 2 * s->audio.bps); - if(audio_capture_channels == 1) { + if (s->audio.ch_count == 1) { // data need to be demultiplexed s->audio.max_size = s->audio.sample_rate * s->audio.bps; s->audio.data = (char *) malloc(s->audio.max_size); diff --git a/src/video_capture/ndi.cpp b/src/video_capture/ndi.cpp index 29ca2c551..2d13518b7 100644 --- a/src/video_capture/ndi.cpp +++ b/src/video_capture/ndi.cpp @@ -49,6 +49,7 @@ #endif #include <Processing.NDI.Lib.h> +#include <algorithm> #include <array> #include <chrono> #include <iostream> @@ -70,8 +71,11 @@ #include "video.h" #include "video_capture.h" +static constexpr const char *MOD_NAME = "[NDI] "; + using std::array; using std::cout; +using std::max; using std::string; using std::chrono::duration_cast; using std::chrono::steady_clock; @@ -199,22 +203,27 @@ static void vidcap_ndi_done(void *state) static void audio_append(struct vidcap_state_ndi *s, NDIlib_audio_frame_v2_t *frame) { - struct audio_desc d{4, frame->sample_rate, static_cast<int>(audio_capture_channels), AC_PCM}; + struct audio_desc d{4, frame->sample_rate, static_cast<int>(audio_capture_channels > 0 ? audio_capture_channels : frame->no_channels), AC_PCM}; if (!audio_desc_eq(d, audio_desc_from_audio_frame(&s->audio[s->audio_buf_idx]))) { free(s->audio[s->audio_buf_idx].data); s->audio[s->audio_buf_idx].bps = 4; s->audio[s->audio_buf_idx].sample_rate = frame->sample_rate; - s->audio[s->audio_buf_idx].ch_count = audio_capture_channels; + s->audio[s->audio_buf_idx].ch_count = d.ch_count; s->audio[s->audio_buf_idx].data_len = 0; s->audio[s->audio_buf_idx].max_size = - 4 * audio_capture_channels * frame->sample_rate / 5; // 200 ms + 4 * d.ch_count * frame->sample_rate / 5; // 200 ms s->audio[s->audio_buf_idx].data = static_cast<char *>(malloc(s->audio[s->audio_buf_idx].max_size)); } + if (frame->no_channels > s->audio[s->audio_buf_idx].ch_count) { + LOG(LOG_LEVEL_WARNING) << MOD_NAME << "Requested " << s->audio[s->audio_buf_idx].ch_count << " channels, stream has only " + << frame->no_channels << "!\n"; + } + for (int i = 0; i < frame->no_samples; ++i) { float *in = frame->p_data + i; - int32_t *out = (int32_t *) s->audio[s->audio_buf_idx].data + i * audio_capture_channels; - for (int j = 0; j < static_cast<int>(audio_capture_channels); ++j) { + int32_t *out = (int32_t *) s->audio[s->audio_buf_idx].data + i * d.ch_count; + for (int j = 0; j < max(d.ch_count, frame->no_channels); ++j) { if (s->audio[s->audio_buf_idx].data_len >= s->audio[s->audio_buf_idx].max_size) { LOG(LOG_LEVEL_WARNING) << "[NDI] Audio frame too small!\n"; return; diff --git a/src/video_capture/testcard.cpp b/src/video_capture/testcard.cpp index f672c6b27..334254751 100644 --- a/src/video_capture/testcard.cpp +++ b/src/video_capture/testcard.cpp @@ -85,7 +85,7 @@ #define AUDIO_BPS 2 #define BUFFER_SEC 1 #define AUDIO_BUFFER_SIZE (AUDIO_SAMPLE_RATE * AUDIO_BPS * \ - audio_capture_channels * BUFFER_SEC) + s->audio.ch_count * BUFFER_SEC) #define MOD_NAME "[testcard] " constexpr video_desc default_format = { 1920, 1080, UYVY, 25.0, INTERLACED_MERGED, 1 }; constexpr size_t headroom = 128; // headroom for cases when dst color_spec has wider block size @@ -316,10 +316,17 @@ static int configure_audio(struct testcard_state *s) Mix_Music *music; ssize_t bytes_written = 0l; + s->audio_data = (char *) calloc(1, AUDIO_BUFFER_SIZE /* 1 sec */); + s->audio_start = 0; + s->audio_end = 0; + s->audio.bps = AUDIO_BPS; + s->audio.ch_count = audio_capture_channels > 0 ? audio_capture_channels : DEFAULT_AUDIO_CAPTURE_CHANNELS; + s->audio.sample_rate = AUDIO_SAMPLE_RATE; + SDL_Init(SDL_INIT_AUDIO); if( Mix_OpenAudio( AUDIO_SAMPLE_RATE, AUDIO_S16LSB, - audio_capture_channels, 4096 ) == -1 ) { + s->audio.ch_count, 4096 ) == -1 ) { fprintf(stderr,"[testcard] error initalizing sound\n"); return -1; } @@ -340,13 +347,6 @@ static int configure_audio(struct testcard_state *s) close(fd); music = Mix_LoadMUS(filename); - s->audio_data = (char *) calloc(1, AUDIO_BUFFER_SIZE /* 1 sec */); - s->audio_start = 0; - s->audio_end = 0; - s->audio.bps = AUDIO_BPS; - s->audio.ch_count = audio_capture_channels; - s->audio.sample_rate = AUDIO_SAMPLE_RATE; - // register grab as a postmix processor if(!Mix_RegisterEffect(MIX_CHANNEL_POST, grab_audio, NULL, s)) { printf("[testcard] Mix_RegisterEffect: %s\n", Mix_GetError()); diff --git a/src/video_capture/testcard2.c b/src/video_capture/testcard2.c index bcca31a04..88c1b7472 100644 --- a/src/video_capture/testcard2.c +++ b/src/video_capture/testcard2.c @@ -67,7 +67,7 @@ #define AUDIO_BPS 2 #define BUFFER_SEC 1 #define AUDIO_BUFFER_SIZE (AUDIO_SAMPLE_RATE * AUDIO_BPS * \ - audio_capture_channels * BUFFER_SEC) + s->audio.ch_count * BUFFER_SEC) void * vidcap_testcard2_thread(void *args); void rgb2yuv422(unsigned char *in, unsigned int width, unsigned int height); @@ -100,22 +100,19 @@ struct testcard_state2 { static int configure_audio(struct testcard_state2 *s) { - int i; + s->audio.bps = AUDIO_BPS; + s->audio.ch_count = audio_capture_channels > 0 > audio_capture_channels : DEFAULT_AUDIO_CAPTURE_CHANNELS; + s->audio.sample_rate = AUDIO_SAMPLE_RATE; s->audio_silence = calloc(1, AUDIO_BUFFER_SIZE /* 1 sec */); s->audio_tone = calloc(1, AUDIO_BUFFER_SIZE /* 1 sec */); short int * data = (short int *) s->audio_tone; - for( i=0; i < (int) AUDIO_BUFFER_SIZE/2; i+=2 ) + for(int i=0; i < (int) AUDIO_BUFFER_SIZE/2; i+=2 ) { data[i] = data[i+1] = (float) sin( ((double)i/(double)200) * M_PI * 2. ) * SHRT_MAX; } - - s->audio.bps = AUDIO_BPS; - s->audio.ch_count = audio_capture_channels; - s->audio.sample_rate = AUDIO_SAMPLE_RATE; - printf("[testcard2] playing audio\n"); return 0; @@ -499,7 +496,7 @@ static void grab_audio(struct testcard_state2 *s) s->audio_remained = (seconds + s->audio_remained) * AUDIO_SAMPLE_RATE - s->audio.data_len; s->audio_remained /= AUDIO_SAMPLE_RATE; - s->audio.data_len *= audio_capture_channels * AUDIO_BPS; + s->audio.data_len *= s->audio.ch_count * AUDIO_BPS; s->last_audio_time = curr_time; } From 9976ad4617f41fae0786efe68c818a9620d4f8e1 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Thu, 10 Sep 2020 10:33:45 +0200 Subject: [PATCH 09/74] RTP UDP: udp_port_pair_is_free() fix Check IPv6 ports if IPv4 address was given but IPv4 mode is not forced (because IPv6 sockets are then opened with IPv4-mapped address). + utilize AI_PASSIVE --- src/rtp/net_udp.cpp | 20 ++++++++++---------- src/rtp/net_udp.h | 2 +- src/rtp/rtp.c | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/rtp/net_udp.cpp b/src/rtp/net_udp.cpp index d961a1e1d..1ce127435 100644 --- a/src/rtp/net_udp.cpp +++ b/src/rtp/net_udp.cpp @@ -68,13 +68,16 @@ #include <chrono> #include <mutex> #include <queue> +#include <string> #include <utility> // std::swap using std::condition_variable; using std::max; using std::mutex; using std::queue; +using std::string; using std::swap; +using std::to_string; using std::unique_lock; #define DEFAULT_MAX_UDP_READER_QUEUE_LEN (1920/3*8*1080/1152) //< 10-bit FullHD frame divided by 1280 MTU packets (minus headers) @@ -1520,17 +1523,16 @@ bool udp_is_ipv6(socket_udp *s) * @retval -1 failed * @retval -2 incorrect service or hostname (not a port number) */ -int udp_port_pair_is_free(const char *addr, int force_ip_version, int even_port) +int udp_port_pair_is_free(int force_ip_version, int even_port) { struct sockaddr *sin; - struct addrinfo hints, *res0; - int err; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = force_ip_version ? (force_ip_version == 6 ? AF_INET6 : AF_INET) : AF_UNSPEC; + struct addrinfo hints{}; + struct addrinfo *res0 = nullptr; + hints.ai_family = force_ip_version == 4 ? AF_INET : AF_INET6; + hints.ai_flags = AI_NUMERICSERV | AI_PASSIVE; hints.ai_socktype = SOCK_DGRAM; - char tx_port_str[7]; - sprintf(tx_port_str, "%u", 5004); - if ((err = getaddrinfo(addr, tx_port_str, &hints, &res0)) != 0) { + string tx_port_str = to_string(5004); + if (int err = 0; (err = getaddrinfo(nullptr, tx_port_str.c_str(), &hints, &res0)) != 0) { /* We should probably try to do a DNS lookup on the name */ /* here, but I'm trying to get the basics going first... */ log_msg(LOG_LEVEL_VERBOSE, "getaddrinfo: %s\n", gai_strerror(err)); @@ -1546,7 +1548,6 @@ int udp_port_pair_is_free(const char *addr, int force_ip_version, int even_port) struct sockaddr_in6 *s_in6 = (struct sockaddr_in6 *) sin; int ipv6only = 0; s_in6->sin6_port = htons(even_port + i); - s_in6->sin6_addr = in6addr_any; fd = socket(AF_INET6, SOCK_DGRAM, 0); if (fd != INVALID_SOCKET) { if (SETSOCKOPT @@ -1560,7 +1561,6 @@ int udp_port_pair_is_free(const char *addr, int force_ip_version, int even_port) } } else { struct sockaddr_in *s_in = (struct sockaddr_in *) sin; - s_in->sin_addr.s_addr = INADDR_ANY; s_in->sin_port = htons(even_port + i); fd = socket(AF_INET, SOCK_DGRAM, 0); } diff --git a/src/rtp/net_udp.h b/src/rtp/net_udp.h index ef1f1f375..a96346e98 100644 --- a/src/rtp/net_udp.h +++ b/src/rtp/net_udp.h @@ -97,7 +97,7 @@ int udp_fd_isset_r(socket_udp *s, struct udp_fd_r *); int udp_recv_data(socket_udp * s, char **buffer); bool udp_not_empty(socket_udp *s, struct timeval *timeout); -int udp_port_pair_is_free(const char *addr, int force_ip_version, int even_port); +int udp_port_pair_is_free(int force_ip_version, int even_port); bool udp_is_ipv6(socket_udp *s); void socket_error(const char *msg, ...); diff --git a/src/rtp/rtp.c b/src/rtp/rtp.c index c28c23ee6..e6ac924a5 100644 --- a/src/rtp/rtp.c +++ b/src/rtp/rtp.c @@ -1113,7 +1113,7 @@ struct rtp *rtp_init_if(const char *addr, const char *iface, if (rx_port == 0) { for (int i = 1<<15; i < 1<<16; i += 2) { // this stuff is not atomic. but... it cannot be done in this way, either - int ret = udp_port_pair_is_free(addr, force_ip_version, i); + int ret = udp_port_pair_is_free(force_ip_version, i); if (ret == 0) { rx_port = i; break; From 3bfa441a809015ecde6ae2e402bc0e12619366d6 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Thu, 10 Sep 2020 11:15:25 +0200 Subject: [PATCH 10/74] udp_port_pair_is_free(): better error handling --- src/rtp/net_udp.cpp | 28 ++++++++++++++++------------ src/rtp/rtp.c | 1 - 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/rtp/net_udp.cpp b/src/rtp/net_udp.cpp index 1ce127435..42c0704eb 100644 --- a/src/rtp/net_udp.cpp +++ b/src/rtp/net_udp.cpp @@ -88,6 +88,8 @@ static void *udp_reader(void *arg); #define IPv4 4 #define IPv6 6 +const constexpr char *MOD_NAME = "[RTP UDP] "; + #ifdef WIN2K_IPV6 const struct in6_addr in6addr_any = { IN6ADDR_ANY_INIT }; #endif @@ -1520,12 +1522,11 @@ bool udp_is_ipv6(socket_udp *s) /** * @retval 0 success - * @retval -1 failed - * @retval -2 incorrect service or hostname (not a port number) + * @retval -1 port pair is not free + * @retval -2 another error */ int udp_port_pair_is_free(int force_ip_version, int even_port) { - struct sockaddr *sin; struct addrinfo hints{}; struct addrinfo *res0 = nullptr; hints.ai_family = force_ip_version == 4 ? AF_INET : AF_INET6; @@ -1535,13 +1536,12 @@ int udp_port_pair_is_free(int force_ip_version, int even_port) if (int err = 0; (err = getaddrinfo(nullptr, tx_port_str.c_str(), &hints, &res0)) != 0) { /* We should probably try to do a DNS lookup on the name */ /* here, but I'm trying to get the basics going first... */ - log_msg(LOG_LEVEL_VERBOSE, "getaddrinfo: %s\n", gai_strerror(err)); - return err == EAI_NONAME ? -2 : -1; - } else { - sin = res0->ai_addr; + LOG(LOG_LEVEL_ERROR) << MOD_NAME << static_cast<const char *>(__func__) << " getaddrinfo: " << gai_strerror(err) << "\n"; + return -2; } for (int i = 0; i < 2; ++i) { + struct sockaddr *sin = res0->ai_addr; fd_t fd; if (sin->sa_family == AF_INET6) { @@ -1553,10 +1553,10 @@ int udp_port_pair_is_free(int force_ip_version, int even_port) if (SETSOCKOPT (fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&ipv6only, sizeof(ipv6only)) != 0) { - socket_error("setsockopt IPV6_V6ONLY"); + socket_error("%s - setsockopt IPV6_V6ONLY", static_cast<const char *>(__func__)); CLOSESOCKET(fd); freeaddrinfo(res0); - return -1; + return -2; } } } else { @@ -1566,15 +1566,19 @@ int udp_port_pair_is_free(int force_ip_version, int even_port) } if (fd == INVALID_SOCKET) { - socket_error("Unable to initialize socket"); + socket_error("%s - unable to initialize socket", static_cast<const char *>(__func__)); freeaddrinfo(res0); - return -1; + return -2; } if (bind(fd, (struct sockaddr *) sin, res0->ai_addrlen) != 0) { freeaddrinfo(res0); CLOSESOCKET(fd); - return -1; + if (errno == EADDRINUSE) { + return -1; + } + socket_error("%s - cannot bind", static_cast<const char *>(__func__)); + return -2; } CLOSESOCKET(fd); diff --git a/src/rtp/rtp.c b/src/rtp/rtp.c index e6ac924a5..4a3d1d546 100644 --- a/src/rtp/rtp.c +++ b/src/rtp/rtp.c @@ -1119,7 +1119,6 @@ struct rtp *rtp_init_if(const char *addr, const char *iface, break; } if (ret == -2) { - log_msg(LOG_LEVEL_ERROR, "Name or service is not known!\n"); free(session); return NULL; } From db4c6e1531bd3bb6d89635fa340c54d755f85be0 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Thu, 10 Sep 2020 16:35:51 +0200 Subject: [PATCH 11/74] SDI audio cap.: updates - when dropping frames, write at least up to available space - allocate the buffer at once (don't realloc) --- src/audio/capture/sdi.cpp | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/audio/capture/sdi.cpp b/src/audio/capture/sdi.cpp index bc9c5b713..f28f35d96 100644 --- a/src/audio/capture/sdi.cpp +++ b/src/audio/capture/sdi.cpp @@ -166,31 +166,26 @@ void sdi_capture_new_incoming_frame(void *state, struct audio_frame *frame) unique_lock<mutex> lk(s->lock); - if( - s->audio_frame[FRAME_CAPTURE].bps != frame->bps || + if (s->audio_frame[FRAME_CAPTURE].bps != frame->bps || s->audio_frame[FRAME_CAPTURE].ch_count != frame->ch_count || - s->audio_frame[FRAME_CAPTURE].sample_rate != frame->sample_rate - ) { + s->audio_frame[FRAME_CAPTURE].sample_rate != frame->sample_rate) { s->audio_frame[FRAME_CAPTURE].bps = frame->bps; s->audio_frame[FRAME_CAPTURE].ch_count = frame->ch_count; s->audio_frame[FRAME_CAPTURE].sample_rate = frame->sample_rate; s->audio_frame[FRAME_CAPTURE].data_len = 0; + s->audio_frame[FRAME_CAPTURE].max_size = frame->bps * frame->ch_count * frame->sample_rate / 1000L * MAX_BUF_SIZE_MS; + s->audio_frame[FRAME_CAPTURE].data = static_cast<char *>(malloc(s->audio_frame[FRAME_CAPTURE].max_size)); } - int needed_size = frame->data_len + s->audio_frame[FRAME_CAPTURE].data_len; - if (needed_size > frame->bps * frame->ch_count * frame->sample_rate / 1000l * MAX_BUF_SIZE_MS) { - fprintf(stderr, "[SDI] Maximal audio buffer length %ld ms exceeded! Dropping samples.\n", - MAX_BUF_SIZE_MS); - } else { - if (needed_size > (int) s->audio_frame[FRAME_CAPTURE].max_size) { - free(s->audio_frame[FRAME_CAPTURE].data); - s->audio_frame[FRAME_CAPTURE].max_size = needed_size; - s->audio_frame[FRAME_CAPTURE].data = (char *) malloc(needed_size); - } - memcpy(s->audio_frame[FRAME_CAPTURE].data + s->audio_frame[FRAME_CAPTURE].data_len, - frame->data, frame->data_len); - s->audio_frame[FRAME_CAPTURE].data_len += frame->data_len; + int len = frame->data_len; + if (len + s->audio_frame[FRAME_CAPTURE].data_len > s->audio_frame[FRAME_CAPTURE].max_size) { + LOG(LOG_LEVEL_WARNING) << "[SDI] Maximal audio buffer length " << MAX_BUF_SIZE_MS << " ms exceeded! Dropping " + << len - (s->audio_frame[FRAME_CAPTURE].max_size - s->audio_frame[FRAME_CAPTURE].data_len) << " samples.\n"; + len = s->audio_frame[FRAME_CAPTURE].max_size - s->audio_frame[FRAME_CAPTURE].data_len; } + memcpy(s->audio_frame[FRAME_CAPTURE].data + s->audio_frame[FRAME_CAPTURE].data_len, + frame->data, len); + s->audio_frame[FRAME_CAPTURE].data_len += len; lk.unlock(); s->audio_frame_ready_cv.notify_one(); From cf07da0d0efa643baf83c8137e4490ba07d81b26 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Thu, 10 Sep 2020 16:53:10 +0200 Subject: [PATCH 12/74] File cap.: fixed a crash Fixes read from a file with PCM codec (frame_size == 0) and perhaps also other cases. + handle and print error of av_samples_get_buffer_size() --- src/video_capture/file.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/video_capture/file.c b/src/video_capture/file.c index 4e78705c1..8f53df663 100644 --- a/src/video_capture/file.c +++ b/src/video_capture/file.c @@ -185,10 +185,14 @@ static void vidcap_file_write_audio(struct vidcap_state_lavf_decoder *s, AVFrame s->audio_frame.data_len += plane_count * bps * s->aud_ctx->frame_size; } else { int data_size = av_samples_get_buffer_size(NULL, s->audio_frame.ch_count, - s->aud_ctx->frame_size, + frame->nb_samples, s->aud_ctx->sample_fmt, 1); - append_audio_frame(&s->audio_frame, (char *) frame->data[0], - data_size); + if (data_size < 0) { + print_libav_error(LOG_LEVEL_WARNING, MOD_NAME " av_samples_get_buffer_size", data_size); + } else { + append_audio_frame(&s->audio_frame, (char *) frame->data[0], + data_size); + } } pthread_mutex_unlock(&s->audio_frame_lock); } From 617d172bc01ec0a44aa446524bbd6620fbd2f1c4 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Thu, 10 Sep 2020 16:58:55 +0200 Subject: [PATCH 13/74] File: print position in sec in debug mode --- src/video_capture/file.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/video_capture/file.c b/src/video_capture/file.c index 8f53df663..ee97da636 100644 --- a/src/video_capture/file.c +++ b/src/video_capture/file.c @@ -266,10 +266,20 @@ static void *vidcap_file_worker(void *state) { } CHECK_FF(ret, FAIL_WORKER); // check the retval of av_read_frame for error other than EOF - log_msg(LOG_LEVEL_DEBUG, MOD_NAME "received %s packet, ID %d, pos %" PRId64 ", size %d\n", + AVRational tb = s->fmt_ctx->streams[pkt.stream_index]->time_base; + + char pts_val[128] = "NO VALUE"; + if (pkt.pts != AV_NOPTS_VALUE) { + snprintf(pts_val, sizeof pts_val, "%" PRId64, pkt.pts); + } + char dts_val[128] = "NO VALUE"; + if (pkt.dts != AV_NOPTS_VALUE) { + snprintf(dts_val, sizeof dts_val, "%" PRId64, pkt.dts); + } + log_msg(LOG_LEVEL_DEBUG, MOD_NAME "received %s packet, ID %d, pos %f (pts %s, dts %s), size %d\n", av_get_media_type_string( s->fmt_ctx->streams[pkt.stream_index]->codecpar->codec_type), - pkt.stream_index, pkt.pos, pkt.size); + pkt.stream_index, (double) pkt.pts * tb.num / tb.den, pts_val, dts_val, pkt.size); if (pkt.stream_index == s->audio_stream_idx) { ret = avcodec_send_packet(s->aud_ctx, &pkt); @@ -554,6 +564,8 @@ static int vidcap_file_init(struct vidcap_params *params, void **state) { s->video_desc.interlacing = PROGRESSIVE; /// @todo other modes } + log_msg(LOG_LEVEL_VERBOSE, MOD_NAME "Capturing audio idx %d, video idx %d\n", s->audio_stream_idx, s->video_stream_idx); + s->last_vid_pts = s->fmt_ctx->streams[s->video_stream_idx]->start_time; playback_register_keyboard_ctl(&s->mod); From f52ee4091236e631d35eeea96243acc181a74b6f Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Fri, 11 Sep 2020 13:31:31 +0200 Subject: [PATCH 14/74] File cap.: limit size of audio frame Limit size of audio frame to 105% of video frame period not to overflow when there are several consecutive audio packets in container (~1 sec) while the SDI audio capture is limited to 100 ms. Partially fixes #86 --- src/video_capture/file.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/video_capture/file.c b/src/video_capture/file.c index ee97da636..03d16a1dd 100644 --- a/src/video_capture/file.c +++ b/src/video_capture/file.c @@ -80,6 +80,7 @@ #include "video.h" #include "video_capture.h" +static const double AUDIO_RATIO = 1.05; ///< at this ratio the audio frame can be longer than the video frame #define MAGIC to_fourcc('u', 'g', 'l', 'f') #define MOD_NAME "[File cap.] " @@ -593,6 +594,28 @@ static void vidcap_file_dispose_audio(struct audio_frame *f) { free(f); } +static struct audio_frame *get_audio(struct vidcap_state_lavf_decoder *s, double video_fps) { + pthread_mutex_lock(&s->audio_frame_lock); + + struct audio_frame *ret = (struct audio_frame *) malloc(sizeof(struct audio_frame)); + memcpy(ret, &s->audio_frame, sizeof *ret); + + // capture more data to ensure the buffer won't grow - it is capped with actually read + // data, still. Moreover there number of audio samples per video frame period may not + // be integer. It shouldn't be much, however, not to confuse adaptible audio buffer. + ret->max_size = + ret->data_len = MIN((int) (AUDIO_RATIO * ret->sample_rate / video_fps) * ret->bps * ret->ch_count , s->audio_frame.data_len); + ret->data = (char *) malloc(ret->max_size); + memcpy(ret->data, s->audio_frame.data, ret->data_len); + + s->audio_frame.data_len -= ret->data_len; + memmove(s->audio_frame.data, s->audio_frame.data + ret->data_len, s->audio_frame.data_len); + + ret->dispose = vidcap_file_dispose_audio; + + pthread_mutex_unlock(&s->audio_frame_lock); + return ret; +} static struct video_frame *vidcap_file_grab(void *state, struct audio_frame **audio) { struct vidcap_state_lavf_decoder *s = (struct vidcap_state_lavf_decoder *) state; @@ -613,11 +636,7 @@ static struct video_frame *vidcap_file_grab(void *state, struct audio_frame **au pthread_mutex_unlock(&s->lock); pthread_cond_signal(&s->frame_consumed); - pthread_mutex_lock(&s->audio_frame_lock); - *audio = audio_frame_copy(&s->audio_frame, false); - (*audio)->dispose = vidcap_file_dispose_audio; - s->audio_frame.data_len = 0; - pthread_mutex_unlock(&s->audio_frame_lock); + *audio = get_audio(s, out->fps); struct timeval t; do { From 69e08d5d900fddc4399acc4ec7c8824fb9001c88 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Fri, 11 Sep 2020 18:52:15 +0200 Subject: [PATCH 15/74] RTP UDP: fixed bind [Win] --- src/rtp/net_udp.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/rtp/net_udp.cpp b/src/rtp/net_udp.cpp index 42c0704eb..cdf546249 100644 --- a/src/rtp/net_udp.cpp +++ b/src/rtp/net_udp.cpp @@ -1572,13 +1572,20 @@ int udp_port_pair_is_free(int force_ip_version, int even_port) } if (bind(fd, (struct sockaddr *) sin, res0->ai_addrlen) != 0) { + int ret = 0; +#ifdef _WIN32 + if (WSAGetLastError() == WSAEADDRINUSE) { +#else + if (errno == EADDRINUSE) { +#endif + ret = -1; + } else { + ret = -2; + socket_error("%s - cannot bind", static_cast<const char *>(__func__)); + } freeaddrinfo(res0); CLOSESOCKET(fd); - if (errno == EADDRINUSE) { - return -1; - } - socket_error("%s - cannot bind", static_cast<const char *>(__func__)); - return -2; + return ret; } CLOSESOCKET(fd); From 625b9d83787fecd72d9b855bea8c6f2e00957939 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Fri, 11 Sep 2020 13:58:08 +0200 Subject: [PATCH 16/74] DELTACAST disp.: properly order includes --- src/video_capture/deltacast.cpp | 42 ++++++++++++++++----------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/src/video_capture/deltacast.cpp b/src/video_capture/deltacast.cpp index 22c96c552..fb38f3f09 100644 --- a/src/video_capture/deltacast.cpp +++ b/src/video_capture/deltacast.cpp @@ -35,36 +35,34 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "host.h" #include "config.h" #include "config_unix.h" #include "config_win32.h" -#include "debug.h" -#include "lib_common.h" -#include "video.h" -#include "video_capture.h" -#include "video_capture_params.h" - -#include "tv.h" +#include <algorithm> +#include <fcntl.h> +#include <semaphore.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifndef WIN32 +#include <sys/ioctl.h> +#include <sys/poll.h> +#endif +#include <sys/stat.h> +#include <sys/time.h> +#include <unistd.h> #include "audio/audio.h" #include "audio/utils.h" +#include "debug.h" #include "deltacast_common.hpp" - -#include <algorithm> -#include <stdio.h> -#include <stdlib.h> -#include <fcntl.h> -#include <unistd.h> -#include <string.h> -#include <sys/stat.h> -#ifndef WIN32 -#include <sys/poll.h> -#include <sys/ioctl.h> -#endif -#include <sys/time.h> -#include <semaphore.h> +#include "host.h" +#include "lib_common.h" +#include "tv.h" +#include "video.h" +#include "video_capture.h" +#include "video_capture_params.h" using namespace std; From a376bfa2d323699bafe95c0222e4818e4e110c8d Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Fri, 11 Sep 2020 08:41:32 +0200 Subject: [PATCH 17/74] GCC 6 compat Use std::is_aggregate with feature testing macro. --- src/audio/codec/libavcodec.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/audio/codec/libavcodec.cpp b/src/audio/codec/libavcodec.cpp index 5caff9ce1..9b5ea146c 100644 --- a/src/audio/codec/libavcodec.cpp +++ b/src/audio/codec/libavcodec.cpp @@ -134,7 +134,9 @@ struct libavcodec_codec_state { bool context_initialized; audio_codec_direction_t direction; }; +#ifdef __cpp_lib_is_aggregate static_assert(is_aggregate_v<libavcodec_codec_state>, "ensure aggregate to allow aggregate initialization"); +#endif #define STR_HELPER(x) #x #define STR(x) STR_HELPER(x) From 0ca9cb3b42099d450b815ca866d3a478770cca6c Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Fri, 11 Sep 2020 16:25:35 +0200 Subject: [PATCH 18/74] Another GCC 6 compat Do not use C++17 if statements with initializer, use declaration with initializer instead. --- src/audio/codec/libavcodec.cpp | 2 +- src/rtp/net_udp.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/audio/codec/libavcodec.cpp b/src/audio/codec/libavcodec.cpp index 9b5ea146c..32a5560e6 100644 --- a/src/audio/codec/libavcodec.cpp +++ b/src/audio/codec/libavcodec.cpp @@ -333,7 +333,7 @@ static bool reinitialize_coder(struct libavcodec_codec_state *s, struct audio_de pthread_mutex_lock(s->libav_global_lock); /* open it */ - if (int ret = 0; (ret = avcodec_open2(s->codec_ctx, s->codec, nullptr)) < 0) { + if (int ret = avcodec_open2(s->codec_ctx, s->codec, nullptr)) { array<char, ERR_MSG_BUF_LEN> errbuf{}; av_strerror(ret, errbuf.data(), errbuf.size()); LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Could not open codec: " << errbuf.data() << "(" << ret << ")\n"; diff --git a/src/rtp/net_udp.cpp b/src/rtp/net_udp.cpp index cdf546249..a58b70ae2 100644 --- a/src/rtp/net_udp.cpp +++ b/src/rtp/net_udp.cpp @@ -1533,7 +1533,7 @@ int udp_port_pair_is_free(int force_ip_version, int even_port) hints.ai_flags = AI_NUMERICSERV | AI_PASSIVE; hints.ai_socktype = SOCK_DGRAM; string tx_port_str = to_string(5004); - if (int err = 0; (err = getaddrinfo(nullptr, tx_port_str.c_str(), &hints, &res0)) != 0) { + if (int err = getaddrinfo(nullptr, tx_port_str.c_str(), &hints, &res0)) { /* We should probably try to do a DNS lookup on the name */ /* here, but I'm trying to get the basics going first... */ LOG(LOG_LEVEL_ERROR) << MOD_NAME << static_cast<const char *>(__func__) << " getaddrinfo: " << gai_strerror(err) << "\n"; From c58a53de4a5f151064a2eacde6c73664fee13735 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Fri, 11 Sep 2020 16:33:54 +0200 Subject: [PATCH 19/74] Utilize print_libav_error --- src/audio/codec/libavcodec.cpp | 42 +++++++++++++++------------------- src/video_capture/file.c | 7 +++--- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/src/audio/codec/libavcodec.cpp b/src/audio/codec/libavcodec.cpp index 32a5560e6..6c6e33e09 100644 --- a/src/audio/codec/libavcodec.cpp +++ b/src/audio/codec/libavcodec.cpp @@ -56,7 +56,6 @@ extern "C" { #include <libavutil/mem.h> } -#include <array> #include <memory> #include <string> #include <type_traits> @@ -138,10 +137,21 @@ struct libavcodec_codec_state { static_assert(is_aggregate_v<libavcodec_codec_state>, "ensure aggregate to allow aggregate initialization"); #endif +/** + * @todo + * Remove and use the global print_libav_error. Dependencies need to be resolved first. + */ +static void print_libav_audio_error(int verbosity, const char *msg, int rc) { + char errbuf[1024]; + av_strerror(rc, errbuf, sizeof(errbuf)); + + log_msg(verbosity, "%s: %s\n", msg, errbuf); +} + #define STR_HELPER(x) #x #define STR(x) STR_HELPER(x) ADD_TO_PARAM("audioenc-frame-duration", "* audioenc-frame-duration=<ms>\n" - " Sets audio encoder frame duration (in ms), default is " STR(LOW_LATENCY_OPUS_FRAME_DURATION) " ms for low-latency-audio\n"); + " Sets audio encoder frame duration (in ms), default is " STR(LOW_LATENCY_AUDIOENC_FRAME_DURATION) " ms for low-latency-audio\n"); /** * Initializates selected audio codec * @param audio_codec requested audio codec @@ -320,10 +330,7 @@ static bool reinitialize_coder(struct libavcodec_codec_state *s, struct audio_de string frame_duration_str{to_string(frame_duration)}; int ret = av_opt_set(s->codec_ctx->priv_data, "frame_duration", frame_duration_str.c_str(), 0); if (ret != 0) { - array<char, ERR_MSG_BUF_LEN> errbuf{}; - av_strerror(ret, errbuf.data(), errbuf.size()); - LOG(LOG_LEVEL_WARNING) << MOD_NAME << "Could set OPUS frame duration: " - << errbuf.data() << " (" << ret << ")\n"; + print_libav_audio_error(LOG_LEVEL_ERROR, "Could not set OPUS frame duration", ret); } } if (s->codec->id == AV_CODEC_ID_FLAC) { @@ -334,9 +341,7 @@ static bool reinitialize_coder(struct libavcodec_codec_state *s, struct audio_de pthread_mutex_lock(s->libav_global_lock); /* open it */ if (int ret = avcodec_open2(s->codec_ctx, s->codec, nullptr)) { - array<char, ERR_MSG_BUF_LEN> errbuf{}; - av_strerror(ret, errbuf.data(), errbuf.size()); - LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Could not open codec: " << errbuf.data() << "(" << ret << ")\n"; + print_libav_audio_error(LOG_LEVEL_ERROR, "Could not open codec", ret); pthread_mutex_unlock(s->libav_global_lock); return false; } @@ -355,10 +360,7 @@ static bool reinitialize_coder(struct libavcodec_codec_state *s, struct audio_de int ret = av_frame_get_buffer(s->av_frame, 0); if (ret != 0) { - array<char, ERR_MSG_BUF_LEN> errbuf{}; - av_strerror(ret, errbuf.data(), errbuf.size()); - LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Could not allocate audio data buffers: " - << errbuf.data() << " (" << ret << ")\n"; + print_libav_audio_error(LOG_LEVEL_ERROR, "Could not allocate audio data buffers", ret); return false; } @@ -478,15 +480,10 @@ static audio_channel *libavcodec_compress(void *state, audio_channel * channel) s->output_channel.duration += s->codec_ctx->frame_size / (double) s->output_channel.sample_rate; } if (ret != AVERROR(EAGAIN) && ret != 0) { - char errbuf[1024]; - av_strerror(ret, errbuf, sizeof(errbuf)); - - log_msg(LOG_LEVEL_WARNING, "Receive packet error: %s %d\n", errbuf, ret); + print_libav_audio_error(LOG_LEVEL_WARNING, "Receive packet error", ret); } } else { - array<char, ERR_MSG_BUF_LEN> errbuf{}; - av_strerror(ret, errbuf.data(), errbuf.size()); - LOG(LOG_LEVEL_WARNING) << MOD_NAME "Error encoding frame: " << errbuf.data() << " (" << ret << ")\n"; + print_libav_audio_error(LOG_LEVEL_ERROR, "Error encoding frame", ret); return {}; } #else @@ -496,10 +493,7 @@ static audio_channel *libavcodec_compress(void *state, audio_channel * channel) int ret = avcodec_encode_audio2(s->codec_ctx, &pkt, s->av_frame, &got_packet); if(ret) { - char errbuf[1024]; - av_strerror(ret, errbuf, sizeof(errbuf)); - fprintf(stderr, "Warning: unable to compress audio: %s\n", - errbuf); + print_libav_audio_error(LOG_MSG_WARNING, MOD_NAME "Warning: unable to compress audio", ret); } if(got_packet) { s->output_channel.data_len += pkt.size; diff --git a/src/video_capture/file.c b/src/video_capture/file.c index 03d16a1dd..ea3f1c31d 100644 --- a/src/video_capture/file.c +++ b/src/video_capture/file.c @@ -480,17 +480,16 @@ static int vidcap_file_init(struct vidcap_params *params, void **state) { /* open input file, and allocate format context */ if ((rc = avformat_open_input(&s->fmt_ctx, s->src_filename, NULL, NULL)) < 0) { - snprintf(errbuf, sizeof errbuf, MOD_NAME "Could not open source file %s: ", s->src_filename); + snprintf(errbuf, sizeof errbuf, MOD_NAME "Could not open source file %s", s->src_filename); } /* retrieve stream information */ if (rc >= 0 && (rc = avformat_find_stream_info(s->fmt_ctx, NULL)) < 0) { - snprintf(errbuf, sizeof errbuf, MOD_NAME "Could not find stream information: \n"); + snprintf(errbuf, sizeof errbuf, MOD_NAME "Could not find stream information"); } if (rc < 0) { - av_strerror(rc, errbuf + strlen(errbuf), sizeof errbuf - strlen(errbuf)); - log_msg(LOG_LEVEL_ERROR, "%s\n", errbuf); + print_libav_error(LOG_LEVEL_ERROR, errbuf, rc); vidcap_file_common_cleanup(s); return VIDCAP_INIT_FAIL; } From 24b6a276536766c36db6f3c7b17b9f1657086cfa Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Fri, 11 Sep 2020 18:04:03 +0200 Subject: [PATCH 20/74] OPUS: set lowdelay application type --- src/audio/codec/libavcodec.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/audio/codec/libavcodec.cpp b/src/audio/codec/libavcodec.cpp index 6c6e33e09..acae2bf9d 100644 --- a/src/audio/codec/libavcodec.cpp +++ b/src/audio/codec/libavcodec.cpp @@ -322,6 +322,12 @@ static bool reinitialize_coder(struct libavcodec_codec_state *s, struct audio_de s->codec_ctx->channel_layout = AV_CH_LAYOUT_MONO; #endif + if (s->codec->id == AV_CODEC_ID_OPUS) { + if (int ret = av_opt_set(s->codec_ctx->priv_data, "application", "lowdelay", 0)) { + print_libav_audio_error(LOG_LEVEL_WARNING, "Could not set OPUS low delay app type", ret); + } + } + if (s->direction == AUDIO_CODER && (commandline_params.find("low-latency-audio"s) != commandline_params.end() || commandline_params.find("audioenc-frame-duration"s) != commandline_params.end())) { double frame_duration = commandline_params.find("audioenc-frame-duration"s) == commandline_params.end() ? From c126f7ac09b1db03b9fb0f64325154dcc92719b9 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Fri, 11 Sep 2020 18:55:08 +0200 Subject: [PATCH 21/74] NET UDP: fixed socket_error() [Win] Format string was printed instead of formatted one. + added WSAEADDRINUSE --- src/rtp/net_udp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rtp/net_udp.cpp b/src/rtp/net_udp.cpp index a58b70ae2..88dcd4e34 100644 --- a/src/rtp/net_udp.cpp +++ b/src/rtp/net_udp.cpp @@ -226,7 +226,7 @@ void socket_error(const char *msg, ...) WSERR(WSAENOTCONN), WSERR(WSAENOTSOCK), WSERR(WSAEOPNOTSUPP), WSERR(WSAESHUTDOWN), WSERR(WSAEWOULDBLOCK), WSERR(WSAEMSGSIZE), WSERR(WSAEHOSTUNREACH), WSERR(WSAECONNABORTED), - WSERR(WSAECONNRESET), + WSERR(WSAECONNRESET), WSERR(WSAEADDRINUSE), WSERR(WSAEADDRNOTAVAIL), WSERR(WSAEAFNOSUPPORT), WSERR(WSAEDESTADDRREQ), WSERR(WSAENETUNREACH), WSERR(WSAETIMEDOUT), WSERR(WSAENOPROTOOPT), @@ -242,7 +242,7 @@ void socket_error(const char *msg, ...) _vsnprintf(buffer, blen, msg, ap); va_end(ap); if (e != WSAECONNRESET) - log_msg(LOG_LEVEL_ERROR, "ERROR: %s, (%d - %s)\n", msg, e, ws_errs[i].errname); + log_msg(LOG_LEVEL_ERROR, "ERROR: %s, (%d - %s)\n", buffer, e, ws_errs[i].errname); #else va_start(ap, msg); vsnprintf(buffer, blen, msg, ap); From 42d9f364bda9d20ba6ba868fb0941dcc1136d020 Mon Sep 17 00:00:00 2001 From: Lukas Rucka <xrucka@fi.muni.cz> Date: Mon, 14 Sep 2020 11:20:29 +0200 Subject: [PATCH 22/74] Fix missleading abbreviations in encoder/decoder option (libavcodec) --- src/video_compress/libavcodec.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/video_compress/libavcodec.cpp b/src/video_compress/libavcodec.cpp index d2d65d537..ddd23140e 100644 --- a/src/video_compress/libavcodec.cpp +++ b/src/video_compress/libavcodec.cpp @@ -262,14 +262,14 @@ static void print_codec_info(AVCodecID id, char *buf, size_t buflen) if (strlen(enc) || strlen(dec)) { strncat(buf, " (", buflen - strlen(buf) - 1); if (strlen(enc)) { - strncat(buf, "enc:", buflen - strlen(buf) - 1); + strncat(buf, "encoders:", buflen - strlen(buf) - 1); strncat(buf, enc, buflen - strlen(buf) - 1); } if (strlen(dec)) { if (strlen(enc)) { strncat(buf, ", ", buflen - strlen(buf) - 1); } - strncat(buf, "dec:", buflen - strlen(buf) - 1); + strncat(buf, "decoders:", buflen - strlen(buf) - 1); strncat(buf, dec, buflen - strlen(buf) - 1); } strncat(buf, ")", buflen - strlen(buf) - 1); @@ -277,7 +277,7 @@ static void print_codec_info(AVCodecID id, char *buf, size_t buflen) #elif LIBAVCODEC_VERSION_MAJOR >= 54 const AVCodec *codec; if ((codec = avcodec_find_encoder(id))) { - strncpy(buf, " (enc:", buflen - 1); + strncpy(buf, " (encoders:", buflen - 1); buf[buflen - 1] = '\0'; do { if (av_codec_is_encoder(codec) && codec->id == id) { @@ -293,7 +293,7 @@ static void print_codec_info(AVCodecID id, char *buf, size_t buflen) } else { strncat(buf, " (", buflen - strlen(buf) - 1); } - strncat(buf, "dec:", buflen - strlen(buf) - 1); + strncat(buf, "decoders:", buflen - strlen(buf) - 1); do { if (av_codec_is_decoder(codec) && codec->id == id) { strncat(buf, " ", buflen - strlen(buf) - 1); From 31d88376823b44c575d26577845fa6ed403efc5b Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Mon, 14 Sep 2020 13:05:10 +0200 Subject: [PATCH 23/74] RTP UDP: fallback format for unknown errors [Win] --- src/rtp/net_udp.cpp | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/rtp/net_udp.cpp b/src/rtp/net_udp.cpp index 88dcd4e34..1d98bec2a 100644 --- a/src/rtp/net_udp.cpp +++ b/src/rtp/net_udp.cpp @@ -64,6 +64,7 @@ #endif #include <algorithm> +#include <array> #include <condition_variable> #include <chrono> #include <mutex> @@ -71,6 +72,7 @@ #include <string> #include <utility> // std::swap +using std::array; using std::condition_variable; using std::max; using std::mutex; @@ -88,6 +90,7 @@ static void *udp_reader(void *arg); #define IPv4 4 #define IPv6 6 +constexpr int ERRBUF_SIZE = 255; const constexpr char *MOD_NAME = "[RTP UDP] "; #ifdef WIN2K_IPV6 @@ -209,9 +212,9 @@ static void udp_clean_async_state(socket_udp *s); void socket_error(const char *msg, ...) { - char buffer[255]; - uint32_t blen = sizeof(buffer) / sizeof(buffer[0]); va_list ap; + array<char, ERRBUF_SIZE> buffer{}; + array<char, ERRBUF_SIZE> strerror_buf{"unknown"}; #ifdef WIN32 #define WSERR(x) {#x,x} @@ -233,26 +236,38 @@ void socket_error(const char *msg, ...) WSERR(0) }; - int i, e = WSAGetLastError(); - i = 0; + int i = 0; + int e = WSAGetLastError(); + if (e == WSAECONNRESET) { + return; + } while (ws_errs[i].errno_code && ws_errs[i].errno_code != e) { i++; } va_start(ap, msg); - _vsnprintf(buffer, blen, msg, ap); + _vsnprintf(buffer.data(), buffer.size(), static_cast<const char *>(msg), ap); va_end(ap); - if (e != WSAECONNRESET) - log_msg(LOG_LEVEL_ERROR, "ERROR: %s, (%d - %s)\n", buffer, e, ws_errs[i].errname); + + if (i == 0) { // let system format the error message + FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // flags + NULL, // lpsource + e, // message id + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), // languageid + strerror_buf.data(), // output buffer + strerror_buf.size(), // size of msgbuf, bytes + NULL); // va_list of arguments + } + const char *errname = i == 0 ? strerror_buf.data() : ws_errs[i].errname; + LOG(LOG_LEVEL_ERROR) << "ERROR: " << buffer.data() << ", (" << e << " - " << errname << ")\n"; #else va_start(ap, msg); - vsnprintf(buffer, blen, msg, ap); + vsnprintf(buffer.data(), buffer.size(), static_cast<const char *>(msg), ap); va_end(ap); - char strerror_buf[255] = ""; #if ! defined _POSIX_C_SOURCE || (_POSIX_C_SOURCE >= 200112L && ! _GNU_SOURCE) - strerror_r(errno, strerror_buf, sizeof strerror_buf); // XSI version - log_msg(LOG_LEVEL_ERROR, "%s: %s\n", buffer, strerror_buf); + strerror_r(errno, strerror_buf.data(), strerror_buf.size()); // XSI version + LOG(LOG_LEVEL_ERROR) << buffer.data() << ": " << strerror_buf.data() << "\n"; #else // GNU strerror_r version - log_msg(LOG_LEVEL_ERROR, "%s: %s\n", buffer, strerror_r(errno, strerror_buf, sizeof strerror_buf)); + LOG(LOG_LEVEL_ERROR) << buffer.data() << ": " << strerror_r(errno, strerror_buf.data(), strerror_buf.size()) << "\n"; #endif #endif } From ec00fb6076d2e6e6b7d21b966e240c56898026d8 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Mon, 14 Sep 2020 14:59:43 +0200 Subject: [PATCH 24/74] AppImage: updater URL fix --- .github/workflows/ccpp.yml | 2 +- data/scripts/Linux-AppImage/create-appimage.sh | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index 9916f934b..3b11a4985 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -103,7 +103,7 @@ jobs: - name: check libstdc++ ABI run: .github/scripts/Linux/check_cxx_abi.sh 3.4.21 1.3.9 bin/* lib/ultragrid/* - name: Create AppImage - run: APPIMAGE=`data/scripts/Linux-AppImage/create-appimage.sh` && mv $APPIMAGE UltraGrid-$VERSION-x86_64.AppImage + run: APPIMAGE=`data/scripts/Linux-AppImage/create-appimage.sh https://github.com/$GITHUB_REPOSITORY/releases/download/$TAG/UltraGrid-$VERSION-x86_64.AppImage.zsync` && mv $APPIMAGE UltraGrid-$VERSION-x86_64.AppImage - name: Upload NDI Build id: upload-ndi if: github.ref == 'refs/heads/ndi-build' diff --git a/data/scripts/Linux-AppImage/create-appimage.sh b/data/scripts/Linux-AppImage/create-appimage.sh index 4e0ad9b3f..fd2c3c87a 100755 --- a/data/scripts/Linux-AppImage/create-appimage.sh +++ b/data/scripts/Linux-AppImage/create-appimage.sh @@ -68,7 +68,11 @@ if [ -n "$appimage_key" ]; then fi wget --no-verbose https://github.com/AppImage/AppImageKit/releases/download/12/appimagetool-x86_64.AppImage -O appimagetool && chmod 755 appimagetool -./appimagetool --sign --comp gzip -u "zsync|https://github.com/${GITHUB_REPOSITORY-CESNET/UltraGrid}/releases/download/nightly/UltraGrid-nightly-x86_64.AppImage.zsync" $APPDIR $APPNAME +UPDATE_INFORMATION= +if [ $# -ge 1 ]; then + UPDATE_INFORMATION="-u zsync|$1" +fi +./appimagetool --sign --comp gzip $UPDATE_INFORMATION $APPDIR $APPNAME ) echo $APPNAME From 3b26787b1a78f4447b8313cff8da719462dbcd4b Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Tue, 15 Sep 2020 09:55:12 +0200 Subject: [PATCH 25/74] Aggregate, multiplier: fixed invalid NULL passed Caused crash for displays that didn't expect NULL (eg. NDI). --- src/video_display/aggregate.c | 2 +- src/video_display/multiplier.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_display/aggregate.c b/src/video_display/aggregate.c index f6491c18a..4f769d8b4 100644 --- a/src/video_display/aggregate.c +++ b/src/video_display/aggregate.c @@ -139,7 +139,7 @@ static void *display_aggregate_init(struct module *parent, const char *fmt, unsi while((item = strtok_r(tmp, "#", &save_ptr))) { char *device; char *config = strdup(item); - char *device_cfg = NULL; + const char *device_cfg = ""; unsigned int dev_flags = 0u; device = config; if(strchr(config, ':')) { diff --git a/src/video_display/multiplier.cpp b/src/video_display/multiplier.cpp index cf361074f..d6bdff660 100644 --- a/src/video_display/multiplier.cpp +++ b/src/video_display/multiplier.cpp @@ -135,7 +135,7 @@ static void *display_multiplier_init(struct module *parent, const char *fmt, uns for(char *token = strtok_r(fmt_copy, "#", &saveptr); token; token = strtok_r(NULL, "#", &saveptr)){ requested_display = token; printf("%s\n", token); - cfg = NULL; + cfg = ""; char *delim = strchr(token, ':'); if (delim) { *delim = '\0'; From b2cb8e3954660573b7b45592f255b9367304c320 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Tue, 15 Sep 2020 10:56:03 +0200 Subject: [PATCH 26/74] Fixed test_video_display() Pass empty string instead of NULL. --- test/test_video_display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_video_display.c b/test/test_video_display.c index f8b0ae3af..d4cdfd168 100644 --- a/test/test_video_display.c +++ b/test/test_video_display.c @@ -49,7 +49,7 @@ int test_video_display(void) printf ("Testing video hardware detection ......................................... "); - if (initialize_video_display(NULL, "none", NULL, 0, NULL, &d) != 0) { + if (initialize_video_display(NULL, "none", "", 0, NULL, &d) != 0) { printf("FAIL\n"); printf(" Cannot intiialize dummy device\n"); return 1; From 3787731274a4c9ddae08f81b6f1133ba20a6d1cb Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Tue, 15 Sep 2020 09:59:33 +0200 Subject: [PATCH 27/74] Video display: updated doc+assert (ensure not-NULL fmt) --- src/video_display.c | 6 ++++-- src/video_display.h | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/video_display.c b/src/video_display.c index 96c99b338..091255ad1 100644 --- a/src/video_display.c +++ b/src/video_display.c @@ -103,8 +103,8 @@ void list_video_display_devices(bool full) /** * @brief Initializes video display. - * @param[in] id video display identifier that will be initialized - * @param[in] fmt command-line entered format string + * @param[in] requested_display video display module name, not NULL + * @param[in] fmt command-line entered format string, not NULL * @param[in] flags bit sum of @ref display_flags * @param[out] state output display state. Defined only if initialization was successful. * @retval 0 if sucessful @@ -114,6 +114,8 @@ void list_video_display_devices(bool full) int initialize_video_display(struct module *parent, const char *requested_display, const char *fmt, unsigned int flags, const char *postprocess, struct display **out) { + assert (requested_display != NULL && fmt != NULL && out != NULL); + if (postprocess && (strcmp(postprocess, "help") == 0 || strcmp(postprocess, "fullhelp") == 0)) { show_vo_postprocess_help(strcmp(postprocess, "fullhelp") == 0); return 1; diff --git a/src/video_display.h b/src/video_display.h index 660bf961a..afc174871 100644 --- a/src/video_display.h +++ b/src/video_display.h @@ -172,8 +172,8 @@ extern int display_init_noerr; void list_video_display_devices(bool full); int initialize_video_display(struct module *parent, - const char *requested_display, const char *fmt, unsigned int flags, - const char *postprocess, struct display **out); + /* not_null */ const char *requested_display, /* not_null */ const char *fmt, + unsigned int flags, const char *postprocess, /* not_null */ struct display **out); bool display_needs_mainloop(struct display *d); void display_run(struct display *d); void display_join(struct display *d); From 3fc08d09752f18e6a974f83172693c342b9d9786 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Tue, 15 Sep 2020 08:49:33 +0200 Subject: [PATCH 28/74] check_cxx_abi.sh: fixes * wrong condition causing whe whole cycle skipping * sort - use version compare (numeric was not correct * missing -a --- .github/scripts/Linux/check_cxx_abi.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/scripts/Linux/check_cxx_abi.sh b/.github/scripts/Linux/check_cxx_abi.sh index e9315284b..ee0ef044a 100755 --- a/.github/scripts/Linux/check_cxx_abi.sh +++ b/.github/scripts/Linux/check_cxx_abi.sh @@ -19,13 +19,13 @@ while test $# -gt 0; do shift continue fi - GLIBCXX_CUR=$(nm $1 | sed -n 's/.*GLIBCXX_\([0-9.]*\).*/\1/p' | sort -n | tail -n 1) - CXX_CUR=$(nm $1 | sed -n 's/.*CXXABI_\([0-9.]*\).*/\1/p' | sort -n | tail -n 1) + GLIBCXX_CUR=$(nm $1 | sed -n 's/.*GLIBCXX_\([0-9.]*\).*/\1/p' | sort -V | tail -n 1) + CXX_CUR=$(nm $1 | sed -n 's/.*CXXABI_\([0-9.]*\).*/\1/p' | sort -V | tail -n 1) if [ -n "$GLIBCXX_CUR" -a "$($SEMVER_CMP $GLIBCXX_CUR $GLIBCXX_MAX)" -gt 0 ]; then echo "$1: GLIBCXX $GLIBCXX_CUR ($GLIBCXX_MAX required)" 1>&2 exit 1 fi - if [ -n "$CXX_CUR" "$($SEMVER_CMP $CXX_CUR $CXX_MAX)" -gt 0 ]; then + if [ -n "$CXX_CUR" -a "$($SEMVER_CMP $CXX_CUR $CXX_MAX)" -gt 0 ]; then echo "$1: CXX $CXX_CUR ($CXX_MAX required)" 1>&2 exit 1 fi From 7f3a49c58dfe95a63686a29190750c55ecc0c1c7 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Tue, 15 Sep 2020 08:59:31 +0200 Subject: [PATCH 29/74] GitHub CI: check also libc ABI [Lin] --- .../Linux/{check_cxx_abi.sh => check_abi.sh} | 21 ++++++++++++++----- .github/workflows/ccpp.yml | 2 +- 2 files changed, 17 insertions(+), 6 deletions(-) rename .github/scripts/Linux/{check_cxx_abi.sh => check_abi.sh} (61%) diff --git a/.github/scripts/Linux/check_cxx_abi.sh b/.github/scripts/Linux/check_abi.sh similarity index 61% rename from .github/scripts/Linux/check_cxx_abi.sh rename to .github/scripts/Linux/check_abi.sh index ee0ef044a..a4f4443ce 100755 --- a/.github/scripts/Linux/check_cxx_abi.sh +++ b/.github/scripts/Linux/check_abi.sh @@ -1,11 +1,15 @@ #!/bin/sh -eu -# Checks libstdc++ ABI version +# Checks libc/ibstdc++ ABI version # see also https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html -GLIBCXX_MAX=$1 -CXX_MAX=$2 -shift 2 +## @todo +## consider removing semver.sh and utilize sort -V to compare + +GLIBC_MAX=$1 +GLIBCXX_MAX=$2 +CXX_MAX=$3 +shift 3 SEMVER_CMP=$(dirname $0)/utils/semver.sh @@ -15,12 +19,19 @@ if [ ! -x $SEMVER_CMP ]; then fi while test $# -gt 0; do - if [ -f $1 ]; then + if [ ! -f $1 ]; then shift continue fi + GLIBC_CUR=$(ldd -r -v $1 | sed -n 's/.*(GLIBC_\([0-9.]*\)).*/\1/p' | sort -V | tail -n 1) + ## @todo + ## perpaps use ldd as well for the remaining 2? GLIBCXX_CUR=$(nm $1 | sed -n 's/.*GLIBCXX_\([0-9.]*\).*/\1/p' | sort -V | tail -n 1) CXX_CUR=$(nm $1 | sed -n 's/.*CXXABI_\([0-9.]*\).*/\1/p' | sort -V | tail -n 1) + if [ -n "$GLIBC_CUR" -a "$($SEMVER_CMP $GLIBC_CUR $GLIBC_MAX)" -gt 0 ]; then + echo "$1: GLIBC $GLIBC_CUR ($GLIBC_MAX required)" 1>&2 + exit 1 + fi if [ -n "$GLIBCXX_CUR" -a "$($SEMVER_CMP $GLIBCXX_CUR $GLIBCXX_MAX)" -gt 0 ]; then echo "$1: GLIBCXX $GLIBCXX_CUR ($GLIBCXX_MAX required)" 1>&2 exit 1 diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index 3b11a4985..257831e94 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -101,7 +101,7 @@ jobs: - name: make distcheck run: make distcheck - name: check libstdc++ ABI - run: .github/scripts/Linux/check_cxx_abi.sh 3.4.21 1.3.9 bin/* lib/ultragrid/* + run: .github/scripts/Linux/check_abi.sh 2.23 3.4.21 1.3.9 bin/* lib/ultragrid/* - name: Create AppImage run: APPIMAGE=`data/scripts/Linux-AppImage/create-appimage.sh https://github.com/$GITHUB_REPOSITORY/releases/download/$TAG/UltraGrid-$VERSION-x86_64.AppImage.zsync` && mv $APPIMAGE UltraGrid-$VERSION-x86_64.AppImage - name: Upload NDI Build From 199f5c65cd976b67c6192d6956347e1a2c9e2492 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Tue, 15 Sep 2020 10:54:33 +0200 Subject: [PATCH 30/74] Multiplier: small improvments --- src/video_display/multiplier.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/video_display/multiplier.cpp b/src/video_display/multiplier.cpp index d6bdff660..8344aa5bf 100644 --- a/src/video_display/multiplier.cpp +++ b/src/video_display/multiplier.cpp @@ -58,6 +58,7 @@ using namespace std; static constexpr unsigned int IN_QUEUE_MAX_BUFFER_LEN = 5; +static constexpr const char *MOD_NAME = "[multiplier] "; static constexpr int SKIP_FIRST_N_FRAMES_IN_STREAM = 5; struct sub_display { @@ -102,8 +103,6 @@ static void *display_multiplier_init(struct module *parent, const char *fmt, uns { struct state_multiplier *s; char *fmt_copy = NULL; - const char *requested_display = NULL; - const char *cfg = NULL; s = new state_multiplier(); @@ -113,7 +112,7 @@ static void *display_multiplier_init(struct module *parent, const char *fmt, uns delete s; return &display_init_noerr; } - + if (isdigit(fmt[0])) { // fork struct state_multiplier *orig; sscanf(fmt, "%p", &orig); @@ -133,9 +132,9 @@ static void *display_multiplier_init(struct module *parent, const char *fmt, uns char *saveptr; for(char *token = strtok_r(fmt_copy, "#", &saveptr); token; token = strtok_r(NULL, "#", &saveptr)){ - requested_display = token; - printf("%s\n", token); - cfg = ""; + LOG(LOG_LEVEL_VERBOSE) << MOD_NAME << "Initializing display " << token << "\n"; + const char *requested_display = token; + const char *cfg = ""; char *delim = strchr(token, ':'); if (delim) { *delim = '\0'; From e935591dd72d80744a93e3db30c47216be1a3802 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Tue, 15 Sep 2020 15:42:51 +0200 Subject: [PATCH 31/74] Screen win.: fixes --- src/video_capture/screen_win.c | 81 ++++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 23 deletions(-) diff --git a/src/video_capture/screen_win.c b/src/video_capture/screen_win.c index dba4be847..f0e1c3e02 100644 --- a/src/video_capture/screen_win.c +++ b/src/video_capture/screen_win.c @@ -47,6 +47,7 @@ #include "config_win32.h" #endif /* HAVE_CONFIG_H */ +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> @@ -55,12 +56,21 @@ #include "host.h" #include "lib_common.h" #include "utils/color_out.h" +#include "utils/hresult.h" #include "video.h" #include "video_capture.h" #include "video_capture_params.h" +#define MOD_NAME "[screen win] " + extern const struct video_capture_info vidcap_dshow_info; +struct vidcap_screen_win_state { + HMODULE screen_cap_lib; + bool filter_registered; + void *dshow_state; +}; + static void show_help() { printf("Screen capture\n"); @@ -69,7 +79,6 @@ static void show_help() color_out(COLOR_OUT_BOLD, "[:width=<w>][:height=<h>][:fps=<f>]\n"); } - static struct vidcap_type * vidcap_screen_win_probe(bool verbose, void (**deleter)(void *)) { struct vidcap_type* vt; @@ -106,7 +115,7 @@ static bool set_key(const char *key, int val) } } DWORD val_dword = val; - if (RegSetValueExA(hKey, key, 0L, REG_DWORD, &val_dword, sizeof val_dword) != ERROR_SUCCESS) { + if (RegSetValueExA(hKey, key, 0L, REG_DWORD, (BYTE *) &val_dword, sizeof val_dword) != ERROR_SUCCESS) { return false; } @@ -161,9 +170,33 @@ static bool vidcap_screen_win_process_params(const char *fmt) return true; } +typedef HRESULT __stdcall (*func)(); -#define CHECK_NOT_NULL_EX(cmd, err_action) do { if ((cmd) == NULL) { log_msg(LOG_LEVEL_ERROR, "[screen] %s\n", #cmd); err_action; } } while(0) -#define CHECK_NOT_NULL(cmd) CHECK_NOT_NULL_EX(cmd, return VIDCAP_INIT_FAIL); +#define CHECK_NOT_NULL_EX(cmd, err_action) do { if ((cmd) == NULL) { log_msg(LOG_LEVEL_ERROR, MOD_NAME "%s\n", #cmd); err_action; } } while(0) +#define CHECK_NOT_NULL(cmd) CHECK_NOT_NULL_EX(cmd, return); +static void cleanup(struct vidcap_screen_win_state *s) { + assert(s != NULL); + + if (s->dshow_state) { + vidcap_dshow_info.done(s->dshow_state); + } + + if (s->filter_registered) { + func unregister_filter = NULL; + CHECK_NOT_NULL(unregister_filter = (func)(void *) GetProcAddress(s->screen_cap_lib, "DllUnregisterServer")); + if (unregister_filter != NULL) { + unregister_filter(); + } + } + + if (s->screen_cap_lib) { + FreeLibrary(s->screen_cap_lib); + } + free(s); +} + +#undef CHECK_NOT_NULL +#define CHECK_NOT_NULL(cmd) CHECK_NOT_NULL_EX(cmd, cleanup(s)); static int vidcap_screen_win_init(struct vidcap_params *params, void **state) { const char *cfg = vidcap_params_get_fmt(params); @@ -177,39 +210,41 @@ static int vidcap_screen_win_init(struct vidcap_params *params, void **state) return VIDCAP_INIT_FAIL; } - HMODULE mod; - CHECK_NOT_NULL(mod = LoadLibraryA("screen-capture-recorder-x64.dll")); - typedef void (*func)(); + struct vidcap_screen_win_state *s = calloc(1, sizeof *s); + + CHECK_NOT_NULL(s->screen_cap_lib = LoadLibraryA("screen-capture-recorder-x64.dll")); func register_filter; - CHECK_NOT_NULL(register_filter = (func) GetProcAddress(mod, "DllRegisterServer")); - register_filter(); - FreeLibrary(mod); + CHECK_NOT_NULL(register_filter = (func)(void *) GetProcAddress(s->screen_cap_lib, "DllRegisterServer")); + HRESULT res = register_filter(); + if (FAILED(res)) { + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Register failed: %s\n", hresult_to_str(res)); + cleanup(s); + return VIDCAP_INIT_FAIL; + } + s->filter_registered = true; struct vidcap_params *params_dshow = vidcap_params_allocate(); vidcap_params_set_device(params_dshow, "dshow:device=screen-capture-recorder"); - int ret = vidcap_dshow_info.init(params_dshow, state); + int ret = vidcap_dshow_info.init(params_dshow, &s->dshow_state); + if (ret != 0) { + log_msg(LOG_LEVEL_ERROR, MOD_NAME "DirectShow init failed: %d\n", ret); + cleanup(s); + return VIDCAP_INIT_FAIL; + } vidcap_params_free_struct(params_dshow); + *state = s; return ret; } -#undef CHECK_NOT_NULL -#define CHECK_NOT_NULL(cmd) CHECK_NOT_NULL_EX(cmd, return); static void vidcap_screen_win_done(void *state) { - vidcap_dshow_info.done(state); - - HMODULE mod; - CHECK_NOT_NULL(mod = LoadLibraryA("screen-capture-recorder-x64.dll")); - typedef void (*func)(); - func unregister_filter; - CHECK_NOT_NULL(unregister_filter = (func) GetProcAddress(mod, "DllUnregisterServer")); - unregister_filter(); - FreeLibrary(mod); + cleanup(state); } static struct video_frame * vidcap_screen_win_grab(void *state, struct audio_frame **audio) { - return vidcap_dshow_info.grab(state, audio); + struct vidcap_screen_win_state *s = state; + return vidcap_dshow_info.grab(s->dshow_state, audio); } static const struct video_capture_info vidcap_screen_win_info = { From 5eec4289fe8f9052ded78c65a9e4b7749e3ddef4 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Tue, 15 Sep 2020 16:34:02 +0200 Subject: [PATCH 32/74] ARM AppImage: removed processing of GUI option --- .github/scripts/Linux/arm/build.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/scripts/Linux/arm/build.sh b/.github/scripts/Linux/arm/build.sh index d9ce905c2..43b53ed6c 100755 --- a/.github/scripts/Linux/arm/build.sh +++ b/.github/scripts/Linux/arm/build.sh @@ -100,9 +100,6 @@ elif [ x"$1" = x"--tool" ]; then TOOL=$2 shift 2 $DIR/bin/$TOOL "$@" -elif [ x"$1" = x"--gui" ]; then - shift - $DIR/bin/uv-qt --with-uv $DIR/uv-wrapper.sh "$@" elif [ x"$1" = x"-h" -o x"$1" = x"--help" ]; then usage exit 0 From 3593df6e08d56079788fc4dd32defa48c5786674 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Wed, 16 Sep 2020 10:36:20 +0200 Subject: [PATCH 33/74] CoreAudio play.: reduce buffer len to 50 ms Original 200 ms looks like quite a huge value compared to other modules that use usually 50. --- src/audio/playback/coreaudio.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/audio/playback/coreaudio.cpp b/src/audio/playback/coreaudio.cpp index 7afe46fff..20c162172 100644 --- a/src/audio/playback/coreaudio.cpp +++ b/src/audio/playback/coreaudio.cpp @@ -71,6 +71,7 @@ using rang::fg; using rang::style; using std::cout; +constexpr int DEFAULT_BUFLEN_MS = 50; #define NO_DATA_STOP_SEC 2 #define MOD_NAME "[CoreAudio play.] " @@ -189,7 +190,7 @@ static int audio_play_ca_reconfigure(void *state, struct audio_desc desc) } { - int buf_len_ms = 200; // 200 ms by default + int buf_len_ms = DEFAULT_BUFLEN_MS; if (get_commandline_param("audio-buffer-len")) { buf_len_ms = atoi(get_commandline_param("audio-buffer-len")); assert(buf_len_ms > 0 && buf_len_ms < 10000); From 745f247dbc3fd22d9dc0d4373cb12aafece71807 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Wed, 16 Sep 2020 11:49:20 +0200 Subject: [PATCH 34/74] Make: distcheck - check module errors This check for errors caused by wrong module dependencies. --- Makefile.in | 1 + src/lib_common.cpp | 10 +++++++++- src/lib_common.h | 2 +- src/main.cpp | 5 ++--- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Makefile.in b/Makefile.in index 8dc8df589..2aab6cd61 100644 --- a/Makefile.in +++ b/Makefile.in @@ -508,6 +508,7 @@ check: tests distcheck: $(TARGET) $(TARGET) --capabilities + $(TARGET) --list-modules [ -z "$(GUI_EXE)" ] || $(GUI_EXE) -h # ------------------------------------------------------------------------------------------------- diff --git a/src/lib_common.cpp b/src/lib_common.cpp index b6a87f4ca..34264fd7b 100644 --- a/src/lib_common.cpp +++ b/src/lib_common.cpp @@ -308,7 +308,12 @@ void list_modules(enum library_class cls, int abi_version, bool full) { } } -void list_all_modules() { +/** + * @retval false if there occurs some problem opening one or more modules, true otherwise + */ +bool list_all_modules() { + bool ret = true; + for (auto cls_it = library_class_info.begin(); cls_it != library_class_info.end(); ++cls_it) { cout << cls_it->second.class_name << "\n"; @@ -322,12 +327,15 @@ void list_all_modules() { } if (!lib_errors.empty()) { + ret = false; cout << rang::style::bold << rang::fg::red << "Errors:\n" << rang::fg::reset << rang::style::reset; for (auto && item : lib_errors) { cout << "\t" << rang::fg::red << item.first << rang::fg::reset << "\n\t\t" << item.second << "\n"; } cout << "\n"; } + + return ret; } map<string, const void *> get_libraries_for_class(enum library_class cls, int abi_version, bool include_hidden) diff --git a/src/lib_common.h b/src/lib_common.h index 8e1094909..672078ac4 100644 --- a/src/lib_common.h +++ b/src/lib_common.h @@ -86,7 +86,7 @@ enum library_class { const void *load_library(const char *name, enum library_class, int abi_version); void register_library(const char *name, const void *info, enum library_class, int abi_version, int hidden); void list_modules(enum library_class, int abi_version, bool full); -void list_all_modules(); +bool list_all_modules(); #ifdef __cplusplus } #endif diff --git a/src/main.cpp b/src/main.cpp index 72baea08b..6475f4b0f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -619,7 +619,7 @@ static bool parse_params(char *optarg) return true; } -#define EXIT(retval) { common_cleanup(init); return retval; } +#define EXIT(expr) { int rc = expr; common_cleanup(init); return rc; } int main(int argc, char *argv[]) { @@ -1068,8 +1068,7 @@ int main(int argc, char *argv[]) video_offset = atoi(optarg) < 0 ? abs(atoi(optarg)) : 0; break; case OPT_LIST_MODULES: - list_all_modules(); - EXIT(EXIT_SUCCESS); + EXIT(list_all_modules() ? EXIT_SUCCESS : EXIT_FAILURE); case OPT_START_PAUSED: start_paused = true; break; From 21b9a8e5e80d44644bf2b8e2a50c8897712a022d Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Wed, 16 Sep 2020 13:43:20 +0200 Subject: [PATCH 35/74] File cap.: use dts if pts is not available --- src/video_capture/file.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/video_capture/file.c b/src/video_capture/file.c index ea3f1c31d..ba3fbcca8 100644 --- a/src/video_capture/file.c +++ b/src/video_capture/file.c @@ -102,7 +102,7 @@ struct vidcap_state_lavf_decoder { bool use_audio; int video_stream_idx, audio_stream_idx; - int64_t last_vid_pts; + int64_t last_vid_pts; ///< last played PTS, if PTS == PTS_NO_VALUE, DTS is stored instead struct video_desc video_desc; @@ -280,7 +280,8 @@ static void *vidcap_file_worker(void *state) { log_msg(LOG_LEVEL_DEBUG, MOD_NAME "received %s packet, ID %d, pos %f (pts %s, dts %s), size %d\n", av_get_media_type_string( s->fmt_ctx->streams[pkt.stream_index]->codecpar->codec_type), - pkt.stream_index, (double) pkt.pts * tb.num / tb.den, pts_val, dts_val, pkt.size); + pkt.stream_index, (double) (pkt.pts == AV_NOPTS_VALUE ? pkt.dts : pkt.pts) + * tb.num / tb.den, pts_val, dts_val, pkt.size); if (pkt.stream_index == s->audio_stream_idx) { ret = avcodec_send_packet(s->aud_ctx, &pkt); @@ -303,7 +304,7 @@ static void *vidcap_file_worker(void *state) { } av_frame_free(&frame); } else if (pkt.stream_index == s->video_stream_idx) { - s->last_vid_pts = pkt.pts; + s->last_vid_pts = pkt.pts == AV_NOPTS_VALUE ? pkt.dts : pkt.pts; struct video_frame *out; if (s->no_decode) { out = vf_alloc_desc(s->video_desc); From adf89101f842c6680a76596a3d513f519a518582 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Wed, 16 Sep 2020 15:44:15 +0200 Subject: [PATCH 36/74] File cap.: parallel decompress --- src/video_capture/file.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/video_capture/file.c b/src/video_capture/file.c index ba3fbcca8..67247cc88 100644 --- a/src/video_capture/file.c +++ b/src/video_capture/file.c @@ -316,6 +316,9 @@ static void *vidcap_file_worker(void *state) { } else { AVFrame * frame = av_frame_alloc(); int got_frame = 0; + + struct timeval t0; + gettimeofday(&t0, NULL); ret = avcodec_send_packet(s->vid_ctx, &pkt); if (ret == 0 || ret == AVERROR(EAGAIN)) { ret = avcodec_receive_frame(s->vid_ctx, frame); @@ -323,9 +326,12 @@ static void *vidcap_file_worker(void *state) { got_frame = 1; } } + struct timeval t1; + gettimeofday(&t1, NULL); if (ret != 0) { print_decoder_error(MOD_NAME, ret); } + log_msg(LOG_LEVEL_VERBOSE, MOD_NAME "Video decompress duration: %f\n", tv_diff(t1, t0)); if (ret < 0 || !got_frame) { if (ret < 0) { @@ -405,6 +411,9 @@ static AVCodecContext *vidcap_file_open_dec_ctx(AVCodec *dec, AVStream *st) { if (!dec_ctx) { return NULL; } + dec_ctx->thread_count = 0; // means auto for most codecs + dec_ctx->thread_type = FF_THREAD_SLICE; + /* Copy codec parameters from input stream to output codec context */ if (avcodec_parameters_to_context(dec_ctx, st->codecpar) < 0) { log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to copy parameters\n"); From deb3a54e36e919f33977f945989dbd65f9d5c6f2 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Wed, 16 Sep 2020 22:19:37 +0200 Subject: [PATCH 37/74] Proxy: initialize_video_display() now needs non-NULL fmt --- src/video_display/proxy.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_display/proxy.cpp b/src/video_display/proxy.cpp index fc8e7f29d..65c432c8f 100644 --- a/src/video_display/proxy.cpp +++ b/src/video_display/proxy.cpp @@ -127,7 +127,7 @@ static void *display_proxy_init(struct module *parent, const char *fmt, unsigned struct state_proxy *s; char *fmt_copy = NULL; const char *requested_display = "gl"; - const char *cfg = NULL; + const char *cfg = ""; int ret; s = new state_proxy(); From e493795472fe7f4518411cf8f84c6533ee6253c6 Mon Sep 17 00:00:00 2001 From: Martin Piatka <mapisvk@gmail.com> Date: Mon, 21 Sep 2020 10:46:06 +0200 Subject: [PATCH 38/74] GUI: Local and Remote labels for previews --- gui/QT/ui/ultragrid_window.ui | 101 ++++++++++++++++++++++------------ 1 file changed, 66 insertions(+), 35 deletions(-) diff --git a/gui/QT/ui/ultragrid_window.ui b/gui/QT/ui/ultragrid_window.ui index c467e69cc..3ad7e6290 100644 --- a/gui/QT/ui/ultragrid_window.ui +++ b/gui/QT/ui/ultragrid_window.ui @@ -6,7 +6,7 @@ <rect> <x>0</x> <y>0</y> - <width>1018</width> + <width>1062</width> <height>711</height> </rect> </property> @@ -17,14 +17,7 @@ <string/> </property> <widget class="QWidget" name="centralwidget"> - <layout class="QVBoxLayout" name="verticalLayout_6" stretch="0,6,0,0"> - <item> - <widget class="QLabel" name="previewLabel"> - <property name="text"> - <string>Preview</string> - </property> - </widget> - </item> + <layout class="QVBoxLayout" name="verticalLayout_6" stretch="6,0,0"> <item> <layout class="QHBoxLayout" name="horizontalLayout"> <item> @@ -37,8 +30,8 @@ </property> <property name="sizeHint" stdset="0"> <size> - <width>0</width> - <height>0</height> + <width>13</width> + <height>13</height> </size> </property> </spacer> @@ -48,33 +41,71 @@ <property name="orientation"> <enum>Qt::Horizontal</enum> </property> - <widget class="PreviewWidget" name="capturePreview" native="true"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> + <widget class="QFrame" name="frame"> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> </property> - <property name="minimumSize"> - <size> - <width>64</width> - <height>64</height> - </size> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="PreviewWidget" name="capturePreview" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>64</width> + <height>64</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="previewLabel"> + <property name="text"> + <string>Local</string> + </property> + </widget> + </item> + </layout> </widget> - <widget class="PreviewWidget" name="displayPreview" native="true"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> + <widget class="QFrame" name="frame_2"> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> </property> - <property name="minimumSize"> - <size> - <width>64</width> - <height>64</height> - </size> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="PreviewWidget" name="displayPreview" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>64</width> + <height>64</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Remote</string> + </property> + </widget> + </item> + </layout> </widget> </widget> </item> @@ -501,8 +532,8 @@ <rect> <x>0</x> <y>0</y> - <width>1018</width> - <height>22</height> + <width>1062</width> + <height>24</height> </rect> </property> <property name="contextMenuPolicy"> From 3660b4006ea7cf8d10b9026624361bb0740361ba Mon Sep 17 00:00:00 2001 From: Martin Piatka <mapisvk@gmail.com> Date: Mon, 21 Sep 2020 12:23:46 +0200 Subject: [PATCH 39/74] Every capture filter: Special case to discard all frames --- src/capture_filter/every.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/capture_filter/every.c b/src/capture_filter/every.c index 85de6c233..1d3abf163 100644 --- a/src/capture_filter/every.c +++ b/src/capture_filter/every.c @@ -66,6 +66,7 @@ static void usage() { printf("every usage:\n"); printf("\tevery:numerator[/denominator]\n\n"); printf("Example: every:2 - every second frame will be dropped\n"); + printf("The special case every:0 can be used to discard all frames\n"); } static int init(struct module *parent, const char *cfg, void **state) @@ -83,7 +84,7 @@ static int init(struct module *parent, const char *cfg, void **state) if(strchr(cfg, '/')) { denom = atoi(strchr(cfg, '/') + 1); } - if (denom > n) { + if (denom > n && n != 0) { log_msg(LOG_LEVEL_ERROR, "Currently, numerator has to be greater " "(or equal, which, however, has a little use) than denominator.\n"); return -1; @@ -117,6 +118,11 @@ static struct video_frame *filter(void *state, struct video_frame *in) { struct state_every *s = state; + if (s->num == 0) { + VIDEO_FRAME_DISPOSE(in); + return NULL; + } + s->current = (s->current + 1) % s->num; if (s->current >= s->denom) { From c4806a37145b68bf892ad4eb6775ef70d0c90da7 Mon Sep 17 00:00:00 2001 From: Martin Piatka <mapisvk@gmail.com> Date: Mon, 21 Sep 2020 12:29:39 +0200 Subject: [PATCH 40/74] GUI: Placeholder "localhost" in destination --- gui/QT/ui/ultragrid_window.ui | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gui/QT/ui/ultragrid_window.ui b/gui/QT/ui/ultragrid_window.ui index 3ad7e6290..0fc5b1853 100644 --- a/gui/QT/ui/ultragrid_window.ui +++ b/gui/QT/ui/ultragrid_window.ui @@ -417,6 +417,9 @@ <property name="text"> <string extracomment="Target IP or host"/> </property> + <property name="placeholderText"> + <string>localhost</string> + </property> </widget> </item> <item row="1" column="0"> From b478599bd3e254da48d2f56f3d0c274df272942a Mon Sep 17 00:00:00 2001 From: Martin Piatka <mapisvk@gmail.com> Date: Mon, 21 Sep 2020 12:30:05 +0200 Subject: [PATCH 41/74] GUI: Prevent local source from appearing in Remote preview when idle --- gui/QT/option/settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/QT/option/settings.cpp b/gui/QT/option/settings.cpp index de1df536c..1d98f6b77 100644 --- a/gui/QT/option/settings.cpp +++ b/gui/QT/option/settings.cpp @@ -288,7 +288,7 @@ std::string Settings::getLaunchParams() const{ std::string Settings::getPreviewParams() const{ std::string out; - out += " --capture-filter preview"; + out += " --capture-filter preview,every:0"; out += getOption("video.source").getLaunchOption(); out += " -d preview"; out += getOption("audio.source").getLaunchOption(); From 1c7b30d34cdf8a8f13397228a8ca911b91e6f2bb Mon Sep 17 00:00:00 2001 From: Martin Piatka <mapisvk@gmail.com> Date: Mon, 21 Sep 2020 14:25:47 +0200 Subject: [PATCH 42/74] main: Fix compilation on glibc >= 2.32 (sys_siglist) --- src/main.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 6475f4b0f..3c68e51b1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -271,14 +271,18 @@ static void crash_signal_handler(int sig) *ptr++ = message1[i]; } #ifndef WIN32 - *ptr++ = ' '; *ptr++ = '('; - for (size_t i = 0; i < sizeof sys_siglist[sig] - 1; ++i) { - if (sys_siglist[sig][i] == '\0') { - break; +#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 32) + const char *sigabbrev = sigabbrev_np(sig); +#else + const char *sigabbrev = sys_siglist[sig]; +#endif + if (sigabbrev != NULL) { + *ptr++ = ' '; *ptr++ = '('; + for (size_t i = 0; sigabbrev[i] != '\0'; ++i) { + *ptr++ = sigabbrev[i]; } - *ptr++ = sys_siglist[sig][i]; + *ptr++ = ')'; } - *ptr++ = ')'; #endif const char message2[] = ".\n\nPlease send a bug report to address "; for (size_t i = 0; i < sizeof message2 - 1; ++i) { From 736cc226a5914b93fb8b953dcc864ff92419b282 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Thu, 17 Sep 2020 08:54:24 +0200 Subject: [PATCH 43/74] Proxy: do not default to GL This somehow prevents unintended use by users. + help --- src/video_display/proxy.cpp | 39 ++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/video_display/proxy.cpp b/src/video_display/proxy.cpp index 65c432c8f..364b2cdb9 100644 --- a/src/video_display/proxy.cpp +++ b/src/video_display/proxy.cpp @@ -51,6 +51,7 @@ #include <cinttypes> #include <condition_variable> #include <chrono> +#include <iostream> #include <list> #include <map> #include <memory> @@ -124,29 +125,31 @@ static void *display_run_worker(void *arg) { static void *display_proxy_init(struct module *parent, const char *fmt, unsigned int flags) { - struct state_proxy *s; char *fmt_copy = NULL; - const char *requested_display = "gl"; + const char *requested_display = ""; const char *cfg = ""; int ret; - s = new state_proxy(); + if (fmt == nullptr || strlen(fmt) == 0 || "help"s == fmt) { + cout << "Proxy is an helper display to combine (blend) multiple incoming streams.\n" + "Please do not use, intended for internal purposes!\n"; + return nullptr; + } - if (fmt && strlen(fmt) > 0) { - if (isdigit(fmt[0])) { // fork - struct state_proxy *orig; - sscanf(fmt, "%p", &orig); - s->common = orig->common; - return s; - } else { - fmt_copy = strdup(fmt); - requested_display = fmt_copy; - char *delim = strchr(fmt_copy, ':'); - if (delim) { - *delim = '\0'; - cfg = delim + 1; - } - } + auto *s = new state_proxy(); + + if (isdigit(fmt[0]) != 0) { // fork + struct state_proxy *orig = nullptr; + sscanf(fmt, "%p", &orig); + s->common = orig->common; + return s; + } + fmt_copy = strdup(fmt); + requested_display = fmt_copy; + char *delim = strchr(fmt_copy, ':'); + if (delim != nullptr) { + *delim = '\0'; + cfg = delim + 1; } s->common = shared_ptr<state_proxy_common>(new state_proxy_common()); ret = initialize_video_display(parent, requested_display, cfg, flags, NULL, &s->common->real_display); From 69a6c7ca0f426456aff2cd14e92de704fc5e2b97 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Thu, 17 Sep 2020 09:19:34 +0200 Subject: [PATCH 44/74] Hide internal/helper displays from cmdline listing --- src/video_display/aggregate.c | 2 +- src/video_display/conference.cpp | 2 +- src/video_display/multiplier.cpp | 2 +- src/video_display/pipe.cpp | 2 +- src/video_display/preview.cpp | 2 +- src/video_display/proxy.cpp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/video_display/aggregate.c b/src/video_display/aggregate.c index 4f769d8b4..ba88e64ec 100644 --- a/src/video_display/aggregate.c +++ b/src/video_display/aggregate.c @@ -398,5 +398,5 @@ static const struct video_display_info display_aggregate_info = { DISPLAY_DOESNT_NEED_MAINLOOP, }; -REGISTER_MODULE(aggregate, &display_aggregate_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION); +REGISTER_HIDDEN_MODULE(aggregate, &display_aggregate_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION); diff --git a/src/video_display/conference.cpp b/src/video_display/conference.cpp index a0f84571e..7fe321737 100644 --- a/src/video_display/conference.cpp +++ b/src/video_display/conference.cpp @@ -717,5 +717,5 @@ static const struct video_display_info display_conference_info = { DISPLAY_DOESNT_NEED_MAINLOOP, }; -REGISTER_MODULE(conference, &display_conference_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION); +REGISTER_HIDDEN_MODULE(conference, &display_conference_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION); diff --git a/src/video_display/multiplier.cpp b/src/video_display/multiplier.cpp index 8344aa5bf..eb6c088af 100644 --- a/src/video_display/multiplier.cpp +++ b/src/video_display/multiplier.cpp @@ -346,5 +346,5 @@ static const struct video_display_info display_multiplier_info = { display_multiplier_needs_mainloop, }; -REGISTER_MODULE(multiplier, &display_multiplier_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION); +REGISTER_HIDDEN_MODULE(multiplier, &display_multiplier_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION); diff --git a/src/video_display/pipe.cpp b/src/video_display/pipe.cpp index 3702946fa..7bba268b1 100644 --- a/src/video_display/pipe.cpp +++ b/src/video_display/pipe.cpp @@ -282,5 +282,5 @@ static const struct video_display_info display_pipe_info = { DISPLAY_DOESNT_NEED_MAINLOOP, }; -REGISTER_MODULE(pipe, &display_pipe_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION); +REGISTER_HIDDEN_MODULE(pipe, &display_pipe_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION); diff --git a/src/video_display/preview.cpp b/src/video_display/preview.cpp index 09c5fdad9..a89320b8f 100644 --- a/src/video_display/preview.cpp +++ b/src/video_display/preview.cpp @@ -292,5 +292,5 @@ static const struct video_display_info display_preview_info = { DISPLAY_DOESNT_NEED_MAINLOOP, }; -REGISTER_MODULE(preview, &display_preview_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION); +REGISTER_HIDDEN_MODULE(preview, &display_preview_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION); diff --git a/src/video_display/proxy.cpp b/src/video_display/proxy.cpp index 364b2cdb9..5432939cc 100644 --- a/src/video_display/proxy.cpp +++ b/src/video_display/proxy.cpp @@ -429,5 +429,5 @@ static const struct video_display_info display_proxy_info = { DISPLAY_DOESNT_NEED_MAINLOOP, }; -REGISTER_MODULE(proxy, &display_proxy_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION); +REGISTER_HIDDEN_MODULE(proxy, &display_proxy_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION); From 9c484e86b3fdcdc2c377cf78077538aa49511597 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Mon, 21 Sep 2020 09:07:18 +0200 Subject: [PATCH 45/74] Lavc common: added get_uv_to_av_conversion() --- src/libavcodec_common.c | 12 ++++++++++++ src/libavcodec_common.h | 1 + 2 files changed, 13 insertions(+) diff --git a/src/libavcodec_common.c b/src/libavcodec_common.c index 122f1579f..cb72e0a8f 100644 --- a/src/libavcodec_common.c +++ b/src/libavcodec_common.c @@ -2059,6 +2059,18 @@ const struct uv_to_av_conversion *get_uv_to_av_conversions() { return uv_to_av_conversions; } +pixfmt_callback_t get_uv_to_av_conversion(codec_t uv_codec, int av_codec) { + for (const struct uv_to_av_conversion *conversions = get_uv_to_av_conversions(); + conversions->func != 0; conversions++) { + if (conversions->dst == av_codec && + conversions->src == uv_codec) { + return conversions->func; + } + } + + return NULL; +} + /** * @brief returns list of available conversion. Terminated by uv_to_av_conversion::uv_codec == VIDEO_CODEC_NONE */ diff --git a/src/libavcodec_common.h b/src/libavcodec_common.h index 6211ce479..6198d57f5 100644 --- a/src/libavcodec_common.h +++ b/src/libavcodec_common.h @@ -192,6 +192,7 @@ struct uv_to_av_conversion { pixfmt_callback_t func; }; const struct uv_to_av_conversion *get_uv_to_av_conversions(void); +pixfmt_callback_t get_uv_to_av_conversion(codec_t uv_codec, int av_codec); typedef void av_to_uv_convert(char * __restrict dst_buffer, AVFrame * __restrict in_frame, int width, int height, int pitch, int * __restrict rgb_shift); typedef av_to_uv_convert *av_to_uv_convert_p; From de8ec48d9e69cbd6d1e0d56d182fbff43c54b0fb Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Mon, 21 Sep 2020 09:08:39 +0200 Subject: [PATCH 46/74] Tests: option to run only standard or unit tests --- test/run_tests.cpp | 48 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 605a681d8..c81a3971c 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -47,6 +47,7 @@ #include <cppunit/extensions/TestFactoryRegistry.h> #include <cppunit/ui/text/TestRunner.h> #endif +#include <iostream> #include "debug.h" #include "host.h" @@ -65,6 +66,8 @@ extern "C" { #include "test_video_display.h" } +using std::clog; + #define TEST_AV_HW 1 /* These globals should be fixed in the future as well */ @@ -84,13 +87,9 @@ void exit_uv(int status) exit(status); } -int main(int argc, char **argv) +static bool run_standard_tests() { bool success = true; - struct init_data *init; - if ((init = common_preinit(argc, argv)) == NULL) { - return 2; - } if (test_bitstream() != 0) success = false; @@ -118,8 +117,13 @@ int main(int argc, char **argv) success = false; #endif + return success; +} + +static bool run_unit_tests() +{ #ifdef HAVE_CPPUNIT - printf("Running CppUnit tests:\n"); + std::clog << "Running CppUnit tests:\n"; // Get the top level suite from the registry CPPUNIT_NS::Test *suite = CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest(); @@ -131,10 +135,36 @@ int main(int argc, char **argv) runner.setOutputter( new CPPUNIT_NS::CompilerOutputter( &runner.result(), CPPUNIT_NS::stdCOut() ) ); // Run the test. - success = runner.run() && success; -#else - printf("CppUnit was not found, skipping CppUnit tests!\n"); + return runner.run(); #endif + std::clog << "CppUnit was not found, skipping CppUnit tests!\n"; + return true; +} + +int main(int argc, char **argv) +{ + struct init_data *init = nullptr; + if ((init = common_preinit(argc, argv)) == nullptr) { + return 2; + } + + bool run_standard = true; + bool run_unit = true; + if (argc == 2) { + run_standard = run_unit = false; + if (strcmp("unit", argv[1]) == 0) { + run_unit = true; + } + if (strcmp("standard", argv[1]) == 0) { + run_standard = true; + } + if (strcmp("all", argv[1]) == 0) { + run_standard = run_unit = true; + } + } + + bool success = (run_standard ? run_standard_tests() : true); + success = (run_unit ? run_unit_tests() : true) && success; common_cleanup(init); From e86e66147c56efe261e149f7537b761da856c398 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Mon, 21 Sep 2020 09:34:25 +0200 Subject: [PATCH 47/74] Lavc: added R10k/R12L conv from/to AV_PIX_FMT_YUV444P16LE + conversion AV_PIX_FMT_YUV444P10LE->R10k --- Makefile.in | 1 + src/libavcodec_common.c | 312 ++++++++++++++++++++++++++++- test/ff_codec_conversions_test.cpp | 184 +++++++++++++++++ test/ff_codec_conversions_test.h | 29 +++ 4 files changed, 520 insertions(+), 6 deletions(-) create mode 100644 test/ff_codec_conversions_test.cpp create mode 100644 test/ff_codec_conversions_test.h diff --git a/Makefile.in b/Makefile.in index 2aab6cd61..2dbb82278 100644 --- a/Makefile.in +++ b/Makefile.in @@ -471,6 +471,7 @@ gui/QT/uv-qt$(APPEXT): $(wildcard $(srcdir)/gui/QT/*.cpp $(srcdir)/gui/QT/*.hpp) TEST_OBJS = $(COMMON_OBJS) \ @TEST_OBJS@ \ test/codec_conversions_test.o \ + test/ff_codec_conversions_test.o \ test/get_framerate_test.o \ test/video_desc_test.o \ test/test_bitstream.o \ diff --git a/src/libavcodec_common.c b/src/libavcodec_common.c index cb72e0a8f..ce9eb0cc5 100644 --- a/src/libavcodec_common.c +++ b/src/libavcodec_common.c @@ -47,7 +47,9 @@ #include "config_win32.h" #endif // HAVE_CONFIG_H +#include <assert.h> #include <stdbool.h> +#include <stdint.h> #ifdef HAVE_SWSCALE #include <libswscale/swscale.h> @@ -78,6 +80,12 @@ #define HAVE_12_AND_14_PLANAR_COLORSPACES 1 #endif +#ifdef WORDS_BIGENDIAN +#define BYTE_SWAP(x) (3 - x) +#else +#define BYTE_SWAP(x) x +#endif + // // UG <-> FFMPEG format translations // @@ -185,6 +193,49 @@ bool libav_codec_has_extradata(codec_t codec) { // // uv_to_av_convert conversions // +// +/* @brief Color space coedfficients - RGB full range to YCbCr bt. 709 limited range + * + * Scaled by 1<<COMP_BASE, footroom 16/255, headroom 235/255 (luma), 240/255 (chroma); limits [2^(depth-8)..255*2^(depth-8)-1] + * matrix Y = [ 0.182586, 0.614231, 0.062007; -0.100643, -0.338572, 0.4392157; 0.4392157, -0.398942, -0.040274 ] + * * [coefficients](https://gist.github.com/yohhoy/dafa5a47dade85d8b40625261af3776a) + * * [SDI limits](https://tech.ebu.ch/docs/r/r103.pdf) + * @todo + * Use this transformations in all conversions. + * @{ + */ +#define COMP_BASE (sizeof(int_fast32_t) == 4 ? 15 : 18) // computation will be less precise when int_fast32_t is 32 bit +typedef int_fast32_t comp_type_t; +static_assert(sizeof(comp_type_t) * 8 >= COMP_BASE + 17, "comp_type_t not wide enough (we are computing in up to 16 bits!)"); +static const comp_type_t y_r = (0.2126*219/255) * (1<<COMP_BASE); +static const comp_type_t y_g = (0.7152*219/255) * (1<<COMP_BASE); +static const comp_type_t y_b = (0.0722*219/255) * (1<<COMP_BASE); +static const comp_type_t cb_r = (-0.2126/1.8556*224/255) * (1<<COMP_BASE); +static const comp_type_t cb_g = (-0.7152/1.8556*224/255) * (1<<COMP_BASE); +static const comp_type_t cb_b = ((1-0.0722)/1.8556*224/255) * (1<<COMP_BASE); +static const comp_type_t cr_r = ((1-0.2126)/1.5748*224/255) * (1<<COMP_BASE); +static const comp_type_t cr_g = (-0.7152/1.5748*224/255) * (1<<COMP_BASE); +static const comp_type_t cr_b = (-0.0722/1.5748*224/255) * (1<<COMP_BASE); +#define RGB_TO_Y_709_SCALED(r, g, b) ((r) * y_r + (g) * y_g + (b) * y_b) +#define RGB_TO_CB_709_SCALED(r, g, b) ((r) * cb_r + (g) * cb_g + (b) * cb_b) +#define RGB_TO_CR_709_SCALED(r, g, b) ((r) * cr_r + (g) * cr_g + (b) * cr_b) + +// matrix Y1^-1 = inv(Y) +static const comp_type_t y_scale = 1.164383 * (1<<COMP_BASE); // precomputed value, Y multiplier is same for all channels +//static const comp_type_t r_y = 1; // during computation already contained in y_scale +//static const comp_type_t r_cb = 0; +static const comp_type_t r_cr = 1.792741 * (1<<COMP_BASE); +//static const comp_type_t g_y = 1; +static const comp_type_t g_cb = -0.213249 * (1<<COMP_BASE); +static const comp_type_t g_cr = -0.532909 * (1<<COMP_BASE); +//static const comp_type_t b_y = 1; +static const comp_type_t b_cb = 2.112402 * (1<<COMP_BASE); +//static const comp_type_t b_cr = 0; +#define YCBCR_TO_R_709_SCALED(y, cb, cr) ((y) /* * r_y */ /* + (cb) * r_cb */ + (cr) * r_cr) +#define YCBCR_TO_G_709_SCALED(y, cb, cr) ((y) /* * g_y */ + (cb) * g_cb + (cr) * g_cr) +#define YCBCR_TO_B_709_SCALED(y, cb, cr) ((y) /* * b_y */ + (cb) * b_cb /* + (cr) * b_cr */) +/// @} + static void uyvy_to_yuv420p(AVFrame * __restrict out_frame, unsigned char * __restrict in_data, int width, int height) { int y; @@ -640,6 +691,125 @@ static void r10k_to_yuv422p10le(AVFrame * __restrict out_frame, unsigned char * } } +static void r10k_to_yuv444p16le(AVFrame * __restrict out_frame, unsigned char * __restrict in_data, int width, int height) +{ + const int src_linesize = vc_get_linesize(width, R10k); + for(int y = 0; y < height; y++) { + uint16_t *dst_y = (uint16_t *) (out_frame->data[0] + out_frame->linesize[0] * y); + uint16_t *dst_cb = (uint16_t *) (out_frame->data[1] + out_frame->linesize[1] * y); + uint16_t *dst_cr = (uint16_t *) (out_frame->data[2] + out_frame->linesize[2] * y); + unsigned char *src = in_data + y * src_linesize; + OPTIMIZED_FOR(int x = 0; x < width; x++){ + comp_type_t r = src[0] << 2 | src[1] >> 6; + comp_type_t g = (src[1] & 0x3F ) << 4 | src[2] >> 4; + comp_type_t b = (src[2] & 0x0F) << 6 | src[3] >> 2; + + comp_type_t res_y = (RGB_TO_Y_709_SCALED(r, g, b) >> (COMP_BASE-6)) + (1<<12); + comp_type_t res_cb = (RGB_TO_CB_709_SCALED(r, g, b) >> (COMP_BASE-6)) + (1<<15); + comp_type_t res_cr = (RGB_TO_CR_709_SCALED(r, g, b) >> (COMP_BASE-6)) + (1<<15); + + *dst_y++ = MIN(MAX(res_y, 1<<12), 235 * (1<<8)); + *dst_cb++ = MIN(MAX(res_cb, 1<<12), 240 * (1<<8)); + *dst_cr++ = MIN(MAX(res_cr, 1<<12), 240 * (1<<8)); + src += 4; + } + } +} + +// RGB full range to YCbCr bt. 709 limited range +static void r12l_to_yuv444p16le(AVFrame * __restrict out_frame, unsigned char * __restrict in_data, int width, int height) +{ +#define WRITE_RES \ + res_y = (RGB_TO_Y_709_SCALED(r, g, b) >> (COMP_BASE-4)) + (1<<12);\ + res_cb = (RGB_TO_CB_709_SCALED(r, g, b) >> (COMP_BASE-4)) + (1<<15);\ + res_cr = (RGB_TO_CR_709_SCALED(r, g, b) >> (COMP_BASE-4)) + (1<<15);\ + *dst_y++ = MIN(MAX(res_y, 1<<12), 235 * (1<<8));\ + *dst_cb++ = MIN(MAX(res_cb, 1<<12), 240 * (1<<8));\ + *dst_cr++ = MIN(MAX(res_cr, 1<<12), 240 * (1<<8)); + + const int src_linesize = vc_get_linesize(width, R12L); + for (int y = 0; y < height; ++y) { + unsigned char *src = in_data + y * src_linesize; + uint16_t *dst_y = (uint16_t *) (out_frame->data[0] + out_frame->linesize[0] * y); + uint16_t *dst_cb = (uint16_t *) (out_frame->data[1] + out_frame->linesize[1] * y); + uint16_t *dst_cr = (uint16_t *) (out_frame->data[2] + out_frame->linesize[2] * y); + + OPTIMIZED_FOR (int x = 0; x < width; x += 8) { + comp_type_t r = 0; + comp_type_t g = 0; + comp_type_t b = 0; + comp_type_t res_y = 0; + comp_type_t res_cb = 0; + comp_type_t res_cr = 0; + + r = src[BYTE_SWAP(0)]; + r |= (src[BYTE_SWAP(1)] & 0xF) << 8; + g = src[BYTE_SWAP(2)] << 4 | src[BYTE_SWAP(1)] >> 4; // g0 + b = src[BYTE_SWAP(3)]; + src += 4; + + b |= (src[BYTE_SWAP(0)] & 0xF) << 8; + WRITE_RES // 0 + r = src[BYTE_SWAP(1)] << 4 | src[BYTE_SWAP(0)] >> 4; // r1 + g = src[BYTE_SWAP(2)]; + g |= (src[BYTE_SWAP(3)] & 0xF) << 8; + b = src[BYTE_SWAP(3)] >> 4; + src += 4; + + b |= src[BYTE_SWAP(0)] << 4; // b1 + WRITE_RES // 1 + r = src[BYTE_SWAP(1)]; + r |= (src[BYTE_SWAP(2)] & 0xF) << 8; + g = src[BYTE_SWAP(3)] << 4 | src[BYTE_SWAP(2)] >> 4; // g2 + src += 4; + + b = src[BYTE_SWAP(0)]; + b |= (src[BYTE_SWAP(1)] & 0xF) << 8; + WRITE_RES // 2 + r = src[BYTE_SWAP(2)] << 4 | src[BYTE_SWAP(1)] >> 4; // r3 + g = src[BYTE_SWAP(3)]; + src += 4; + + g |= (src[BYTE_SWAP(0)] & 0xF) << 8; + b = src[BYTE_SWAP(1)] << 4 | src[BYTE_SWAP(0)] >> 4; // b3 + WRITE_RES // 3 + r = src[BYTE_SWAP(2)]; + r |= (src[BYTE_SWAP(3)] & 0xF) << 8; + g = src[BYTE_SWAP(3)] >> 4; + src += 4; + + g |= src[BYTE_SWAP(0)] << 4; // g4 + b = src[BYTE_SWAP(1)]; + b |= (src[BYTE_SWAP(2)] & 0xF) << 8; + WRITE_RES // 4 + r = src[BYTE_SWAP(3)] << 4 | src[BYTE_SWAP(2)] >> 4; // r5 + src += 4; + + g = src[BYTE_SWAP(0)]; + g |= (src[BYTE_SWAP(1)] & 0xF) << 8; + b = src[BYTE_SWAP(2)] << 4 | src[BYTE_SWAP(1)] >> 4; // b5 + WRITE_RES // 5 + r = src[BYTE_SWAP(3)]; + src += 4; + + r |= (src[BYTE_SWAP(0)] & 0xF) << 8; + g = src[BYTE_SWAP(1)] << 4 | src[BYTE_SWAP(0)] >> 4; // g6 + b = src[BYTE_SWAP(2)]; + b |= (src[BYTE_SWAP(3)] & 0xF) << 8; + WRITE_RES // 6 + r = src[BYTE_SWAP(3)] >> 4; + src += 4; + + r |= src[BYTE_SWAP(0)] << 4; // r7 + g = src[BYTE_SWAP(1)]; + g |= (src[BYTE_SWAP(2)] & 0xF) << 8; + b = src[BYTE_SWAP(3)] << 4 | src[BYTE_SWAP(2)] >> 4; // b7 + WRITE_RES // 7 + src += 4; + } + } +} + static void rgb_to_bgr0(AVFrame * __restrict out_frame, unsigned char * __restrict in_data, int width, int height) { int src_linesize = vc_get_linesize(width, RGB); @@ -897,6 +1067,105 @@ static void gbrp10le_to_r10k(char * __restrict dst_buffer, AVFrame * __restrict } } +static void yuv444p16le_to_r10k(char * __restrict dst_buffer, AVFrame * __restrict frame, + int width, int height, int pitch, int * __restrict rgb_shift) +{ + UNUSED(rgb_shift); + for (int y = 0; y < height; ++y) { + uint16_t *src_y = (uint16_t *) (frame->data[0] + frame->linesize[0] * y); + uint16_t *src_cb = (uint16_t *) (frame->data[1] + frame->linesize[1] * y); + uint16_t *src_cr = (uint16_t *) (frame->data[2] + frame->linesize[2] * y); + unsigned char *dst = (unsigned char *) dst_buffer + y * pitch; + + OPTIMIZED_FOR (int x = 0; x < width; ++x) { + comp_type_t y = (y_scale * (*src_y++ - (1<<12))); + comp_type_t cr = *src_cr++ - (1<<15); + comp_type_t cb = *src_cb++ - (1<<15); + + comp_type_t r = YCBCR_TO_R_709_SCALED(y, cb, cr) >> (COMP_BASE+6); + comp_type_t g = YCBCR_TO_G_709_SCALED(y, cb, cr) >> (COMP_BASE+6); + comp_type_t b = YCBCR_TO_B_709_SCALED(y, cb, cr) >> (COMP_BASE+6); + // r g b is now on 10 bit scale + + r = MIN((255<<2) - 1, MAX(1<<2, r)); + g = MIN((255<<2) - 1, MAX(1<<2, g)); + b = MIN((255<<2) - 1, MAX(1<<2, b)); + + *dst++ = r >> 2; + *dst++ = (r & 0x3) << 6 | g >> 4; + *dst++ = (g & 0xF) << 4 | b >> 6; + *dst++ = (b & 0x3F) << 2; + } + } +} + +static void yuv444p16le_to_r12l(char * __restrict dst_buffer, AVFrame * __restrict frame, + int width, int height, int pitch, int * __restrict rgb_shift) +{ + UNUSED(rgb_shift); + for (int y = 0; y < height; ++y) { + uint16_t *src_y = (uint16_t *) (frame->data[0] + frame->linesize[0] * y); + uint16_t *src_cb = (uint16_t *) (frame->data[1] + frame->linesize[1] * y); + uint16_t *src_cr = (uint16_t *) (frame->data[2] + frame->linesize[2] * y); + unsigned char *dst = (unsigned char *) dst_buffer + y * pitch; + + OPTIMIZED_FOR (int x = 0; x < width; x += 8) { + comp_type_t r[8]; + comp_type_t g[8]; + comp_type_t b[8]; + OPTIMIZED_FOR (int j = 0; j < 8; ++j) { + comp_type_t y = (y_scale * (*src_y++ - (1<<12))); + comp_type_t cr = *src_cr++ - (1<<15); + comp_type_t cb = *src_cb++ - (1<<15); + comp_type_t rr = YCBCR_TO_R_709_SCALED(y, cb, cr) >> (COMP_BASE+4); + comp_type_t gg = YCBCR_TO_G_709_SCALED(y, cb, cr) >> (COMP_BASE+4); + comp_type_t bb = YCBCR_TO_B_709_SCALED(y, cb, cr) >> (COMP_BASE+4); + r[j] = MIN((255<<4) - 1, MAX(1<<4, rr)); + g[j] = MIN((255<<4) - 1, MAX(1<<4, gg)); + b[j] = MIN((255<<4) - 1, MAX(1<<4, bb)); + } + + dst[BYTE_SWAP(0)] = r[0] & 0xff; + dst[BYTE_SWAP(1)] = (g[0] & 0xf) << 4 | r[0] >> 8; + dst[BYTE_SWAP(2)] = g[0] >> 4; + dst[BYTE_SWAP(3)] = b[0] & 0xff; + dst[4 + BYTE_SWAP(0)] = (r[1] & 0xf) << 4 | b[0] >> 8; + dst[4 + BYTE_SWAP(1)] = r[1] >> 4; + dst[4 + BYTE_SWAP(2)] = g[1] & 0xff; + dst[4 + BYTE_SWAP(3)] = (b[1] & 0xf) << 4 | g[1] >> 8; + dst[8 + BYTE_SWAP(0)] = b[1] >> 4; + dst[8 + BYTE_SWAP(1)] = r[2] & 0xff; + dst[8 + BYTE_SWAP(2)] = (g[2] & 0xf) << 4 | r[2] >> 8; + dst[8 + BYTE_SWAP(3)] = g[2] >> 4; + dst[12 + BYTE_SWAP(0)] = b[2] & 0xff; + dst[12 + BYTE_SWAP(1)] = (r[3] & 0xf) << 4 | b[2] >> 8; + dst[12 + BYTE_SWAP(2)] = r[3] >> 4; + dst[12 + BYTE_SWAP(3)] = g[3] & 0xff; + dst[16 + BYTE_SWAP(0)] = (b[3] & 0xf) << 4 | g[3] >> 8; + dst[16 + BYTE_SWAP(1)] = b[3] >> 4; + dst[16 + BYTE_SWAP(2)] = r[4] & 0xff; + dst[16 + BYTE_SWAP(3)] = (g[4] & 0xf) << 4 | r[4] >> 8; + dst[20 + BYTE_SWAP(0)] = g[4] >> 4; + dst[20 + BYTE_SWAP(1)] = b[4] & 0xff; + dst[20 + BYTE_SWAP(2)] = (r[5] & 0xf) << 4 | b[4] >> 8; + dst[20 + BYTE_SWAP(3)] = r[5] >> 4;; + dst[24 + BYTE_SWAP(0)] = g[5] & 0xff; + dst[24 + BYTE_SWAP(1)] = (b[5] & 0xf) << 4 | g[5] >> 8; + dst[24 + BYTE_SWAP(2)] = b[5] >> 4; + dst[24 + BYTE_SWAP(3)] = r[6] & 0xff; + dst[28 + BYTE_SWAP(0)] = (g[6] & 0xf) << 4 | r[6] >> 8; + dst[28 + BYTE_SWAP(1)] = g[6] >> 4; + dst[28 + BYTE_SWAP(2)] = b[6] & 0xff; + dst[28 + BYTE_SWAP(3)] = (r[7] & 0xf) << 4 | b[6] >> 8; + dst[32 + BYTE_SWAP(0)] = r[7] >> 4; + dst[32 + BYTE_SWAP(1)] = g[7] & 0xff; + dst[32 + BYTE_SWAP(2)] = (b[7] & 0xf) << 4 | g[7] >> 8; + dst[32 + BYTE_SWAP(3)] = b[7] >> 4; + dst += 36; + } + } +} + static void gbrp10le_to_rgb(char * __restrict dst_buffer, AVFrame * __restrict frame, int width, int height, int pitch, int * __restrict rgb_shift) { @@ -931,12 +1200,6 @@ static void gbrp10le_to_rgba(char * __restrict dst_buffer, AVFrame * __restrict } } -#ifdef WORDS_BIGENDIAN -#define BYTE_SWAP(x) (3 - x) -#else -#define BYTE_SWAP(x) x -#endif - #ifdef HAVE_12_AND_14_PLANAR_COLORSPACES static void gbrp12le_to_r12l(char * __restrict dst_buffer, AVFrame * __restrict frame, int width, int height, int pitch, int * __restrict rgb_shift) @@ -1853,6 +2116,38 @@ static inline void yuv422p10le_to_rgb32(char * __restrict dst_buffer, AVFrame * yuv422p10le_to_rgb(dst_buffer, in_frame, width, height, pitch, rgb_shift, true); } +/// @todo this is almost identical to yuv444p16le_to_r10k except constants, merge +static void yuv444p10le_to_r10k(char * __restrict dst_buffer, AVFrame * __restrict frame, + int width, int height, int pitch, int * __restrict rgb_shift) +{ + UNUSED(rgb_shift); + for (int y = 0; y < height; ++y) { + uint16_t *src_y = (uint16_t *) (frame->data[0] + frame->linesize[0] * y); + uint16_t *src_cb = (uint16_t *) (frame->data[1] + frame->linesize[1] * y); + uint16_t *src_cr = (uint16_t *) (frame->data[2] + frame->linesize[2] * y); + unsigned char *dst = (unsigned char *) dst_buffer + y * pitch; + + OPTIMIZED_FOR (int x = 0; x < width; ++x) { + comp_type_t y = (y_scale * (*src_y++ - (1<<6))); + comp_type_t cr = *src_cr++ - (1<<9); + comp_type_t cb = *src_cb++ - (1<<9); + + comp_type_t r = YCBCR_TO_R_709_SCALED(y, cb, cr) >> COMP_BASE; + comp_type_t g = YCBCR_TO_G_709_SCALED(y, cb, cr) >> COMP_BASE; + comp_type_t b = YCBCR_TO_B_709_SCALED(y, cb, cr) >> COMP_BASE; + // r g b is now on 10 bit scale + + r = MIN((255<<2) - 1, MAX(1<<2, r)); + g = MIN((255<<2) - 1, MAX(1<<2, g)); + b = MIN((255<<2) - 1, MAX(1<<2, b)); + + *dst++ = r >> 2; + *dst++ = (r & 0x3) << 6 | g >> 4; + *dst++ = (g & 0xF) << 4 | b >> 6; + *dst++ = (b & 0x3F) << 2; + } + } +} static inline void yuv444p10le_to_rgb24(char * __restrict dst_buffer, AVFrame * __restrict in_frame, int width, int height, int pitch, int * __restrict rgb_shift) @@ -2035,6 +2330,8 @@ const struct uv_to_av_conversion *get_uv_to_av_conversions() { { v210, AV_PIX_FMT_YUV422P10LE, v210_to_yuv422p10le }, { v210, AV_PIX_FMT_YUV444P10LE, v210_to_yuv444p10le }, { v210, AV_PIX_FMT_YUV444P16LE, v210_to_yuv444p16le }, + { R10k, AV_PIX_FMT_YUV444P16LE, r10k_to_yuv444p16le }, + { R12L, AV_PIX_FMT_YUV444P16LE, r12l_to_yuv444p16le }, #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(55, 15, 100) // FFMPEG commit c2869b4640f { v210, AV_PIX_FMT_P010LE, v210_to_p010le }, #endif @@ -2088,6 +2385,7 @@ const struct av_to_uv_conversion *get_av_to_uv_conversions() { {AV_PIX_FMT_YUV444P10LE, v210, yuv444p10le_to_v210, true}, {AV_PIX_FMT_YUV444P16LE, v210, yuv444p16le_to_v210, true}, {AV_PIX_FMT_YUV444P10LE, UYVY, yuv444p10le_to_uyvy, false}, + {AV_PIX_FMT_YUV444P10LE, R10k, yuv444p10le_to_r10k, false}, {AV_PIX_FMT_YUV444P10LE, RGB, yuv444p10le_to_rgb24, false}, {AV_PIX_FMT_YUV444P10LE, RGBA, yuv444p10le_to_rgb32, false}, #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(55, 15, 100) // FFMPEG commit c2869b4640f @@ -2131,6 +2429,8 @@ const struct av_to_uv_conversion *get_av_to_uv_conversions() { {AV_PIX_FMT_RGB24, RGB, memcpy_data, true}, {AV_PIX_FMT_RGB24, RGBA, rgb24_to_rgb32, false}, {AV_PIX_FMT_GBRP10LE, R10k, gbrp10le_to_r10k, true}, + {AV_PIX_FMT_YUV444P16LE, R10k, yuv444p16le_to_r10k, false}, + {AV_PIX_FMT_YUV444P16LE, R12L, yuv444p16le_to_r12l, false}, {AV_PIX_FMT_GBRP10LE, RGB, gbrp10le_to_rgb, false}, {AV_PIX_FMT_GBRP10LE, RGBA, gbrp10le_to_rgba, false}, #ifdef HAVE_12_AND_14_PLANAR_COLORSPACES diff --git a/test/ff_codec_conversions_test.cpp b/test/ff_codec_conversions_test.cpp new file mode 100644 index 000000000..8754b7de8 --- /dev/null +++ b/test/ff_codec_conversions_test.cpp @@ -0,0 +1,184 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#include "config_unix.h" +#include "config_win32.h" +#endif + +#if defined HAVE_CPPUNIT && defined HAVE_LAVC + +#include <algorithm> +#include <array> +#include <cppunit/config/SourcePrefix.h> +#include <random> +#include <string> +#include <vector> + +#include "ff_codec_conversions_test.h" +#include "libavcodec_common.h" +#include "video_capture/testcard_common.h" +#include "video_codec.h" + +using std::array; +using std::copy; +using std::default_random_engine; +using std::max; +using std::to_string; +using std::vector; + +// Registers the fixture into the 'registry' +CPPUNIT_TEST_SUITE_REGISTRATION( ff_codec_conversions_test ); + +ff_codec_conversions_test::ff_codec_conversions_test() +{ +} + +ff_codec_conversions_test::~ff_codec_conversions_test() +{ +} + +void +ff_codec_conversions_test::setUp() +{ +} + +void +ff_codec_conversions_test::tearDown() +{ +} + +void +ff_codec_conversions_test::test_yuv444p16le_from_to_r10k() +{ + using namespace std::string_literals; + + constexpr int width = 1920; + constexpr int height = 1080; + vector <unsigned char> rgba_buf(width * height * 4); + + /// @todo Use 10-bit natively + auto test_pattern = [&] { + vector <unsigned char> r10k_buf(width * height * 4); + copy(rgba_buf.begin(), rgba_buf.end(), r10k_buf.begin()); + toR10k(r10k_buf.data(), width, height); + + AVFrame frame; + frame.format = AV_PIX_FMT_YUV444P16LE; + frame.width = width; + frame.height = height; + + /* the image can be allocated by any means and av_image_alloc() is + * just the most convenient way if av_malloc() is to be used */ + assert(av_image_alloc(frame.data, frame.linesize, + width, height, (AVPixelFormat) frame.format, 32) >= 0); + + auto from_conv = get_uv_to_av_conversion(R10k, frame.format); + auto to_conv = get_av_to_uv_conversion(frame.format, R10k); + assert(to_conv != nullptr && from_conv != nullptr); + + from_conv(&frame, r10k_buf.data(), width, height); + to_conv(reinterpret_cast<char*>(r10k_buf.data()), &frame, width, height, vc_get_linesize(width, R10k), nullptr); + + av_freep(frame.data); + + vector <unsigned char> rgba_buf_res(width * height * 4); + vc_copyliner10k(rgba_buf_res.data(), r10k_buf.data(), height * vc_get_linesize(width, RGBA), 0, 8, 16); + + int max_diff = 0; + for (size_t i = 0; i < width * height; ++i) { + for (int j = 0; j < 3; ++j) { + max_diff = max<int>(max_diff, abs(rgba_buf[4 * i + j] - rgba_buf_res[4 * i + j])); + //fprintf(stderr, "%d %d\n", (int) rgba_buf[4 * i + j], (int) rgba_buf_res[4 * i + j]); + } + //fprintf(stderr, "R in 10 bits = %d\n", (int) (r10k_buf[4 * i] << 2) + (r10k_buf[4 * i + 1] >> 6)); + } + + if (getenv("DEBUG_DUMP") != nullptr) { + FILE *out = fopen("out.rgba","w"); + fwrite(rgba_buf_res.data(), width * height * 4, 1, out); + fclose(out); + } + + CPPUNIT_ASSERT_MESSAGE("Maximal allowed difference 1, found "s + to_string(max_diff), max_diff <= 1); + }; + + int i = 0; + for_each(rgba_buf.begin(), rgba_buf.end(), [&](unsigned char & c) { c = (i++ / 4) % 0x100; }); + test_pattern(); + + array<unsigned char, 4> pattern{ 0xFFU, 0, 0, 0xFFU }; + for_each(rgba_buf.begin(), rgba_buf.end(), [&](unsigned char & c) { c = pattern[i++ % 4]; }); + test_pattern(); + + default_random_engine rand_gen; + for_each(rgba_buf.begin(), rgba_buf.end(), [&](unsigned char & c) { c = rand_gen() % 0x100; }); + test_pattern(); +} + +void +ff_codec_conversions_test::test_yuv444p16le_from_to_r12l() +{ + using namespace std::string_literals; + + constexpr int width = 1920; + constexpr int height = 1080; + vector <unsigned char> rgb_buf(width * height * 3); + + /// @todo Use 12-bit natively + auto test_pattern = [&] { + vector <unsigned char> r12l_buf(vc_get_datalen(width, height, R12L)); + decoder_t rgb_to_r12l = get_decoder_from_to(RGB, R12L, true); + rgb_to_r12l(r12l_buf.data(), rgb_buf.data(), vc_get_datalen(width, height, R12L), 0, 8, 16); + + AVFrame frame; + frame.format = AV_PIX_FMT_YUV444P16LE; + frame.width = width; + frame.height = height; + + /* the image can be allocated by any means and av_image_alloc() is + * just the most convenient way if av_malloc() is to be used */ + assert(av_image_alloc(frame.data, frame.linesize, + width, height, (AVPixelFormat) frame.format, 32) >= 0); + + auto from_conv = get_uv_to_av_conversion(R12L, frame.format); + auto to_conv = get_av_to_uv_conversion(frame.format, R12L); + assert(to_conv != nullptr && from_conv != nullptr); + + from_conv(&frame, r12l_buf.data(), width, height); + to_conv(reinterpret_cast<char*>(r12l_buf.data()), &frame, width, height, vc_get_linesize(width, R12L), nullptr); + + av_freep(frame.data); + + vector <unsigned char> rgb_buf_res(width * height * 3); + decoder_t r12l_to_rgb = get_decoder_from_to(R12L, RGB, true); + r12l_to_rgb(rgb_buf_res.data(), r12l_buf.data(), vc_get_datalen(width, height, RGB), 0, 8, 16); + + int max_diff = 0; + for (size_t i = 0; i < width * height; ++i) { + for (int j = 0; j < 3; ++j) { + max_diff = max<int>(max_diff, abs(rgb_buf[3 * i + j] - rgb_buf_res[3 * i + j])); + } + } + + if (getenv("DEBUG_DUMP") != nullptr) { + FILE *out = fopen("out.rgb","w"); + fwrite(rgb_buf_res.data(), width * height * 3, 1, out); + fclose(out); + } + + CPPUNIT_ASSERT_MESSAGE("Maximal allowed difference 1, found "s + to_string(max_diff), max_diff <= 1); + }; + + int i = 0; + array<unsigned char, 3> pattern{ 0xFFU, 0, 0 }; + for_each(rgb_buf.begin(), rgb_buf.end(), [&](unsigned char & c) { c = pattern[i++ % 3]; }); + test_pattern(); + + for_each(rgb_buf.begin(), rgb_buf.end(), [&](unsigned char & c) { c = (i++ / 3) % 0x100; }); + test_pattern(); + + default_random_engine rand_gen; + for_each(rgb_buf.begin(), rgb_buf.end(), [&](unsigned char & c) { c = rand_gen() % 0x100; }); + test_pattern(); +} + +#endif // defined HAVE_CPPUNIT && HAVE_LAVC diff --git a/test/ff_codec_conversions_test.h b/test/ff_codec_conversions_test.h new file mode 100644 index 000000000..9af83b004 --- /dev/null +++ b/test/ff_codec_conversions_test.h @@ -0,0 +1,29 @@ +#ifndef FF_CODEC_CONVERSIONS_TEST_H_277D34B0_7056_45BF_9A47_EA2AD1DEA846 +#define FF_CODEC_CONVERSIONS_TEST_H_277D34B0_7056_45BF_9A47_EA2AD1DEA846 + +#include "config.h" + +#ifdef HAVE_LAVC + +#include <cppunit/extensions/HelperMacros.h> + +class ff_codec_conversions_test : public CPPUNIT_NS::TestFixture +{ + CPPUNIT_TEST_SUITE( ff_codec_conversions_test ); + CPPUNIT_TEST( test_yuv444p16le_from_to_r10k ); + CPPUNIT_TEST( test_yuv444p16le_from_to_r12l ); + CPPUNIT_TEST_SUITE_END(); + +public: + ff_codec_conversions_test(); + ~ff_codec_conversions_test(); + void setUp(); + void tearDown(); + + void test_yuv444p16le_from_to_r10k(); + void test_yuv444p16le_from_to_r12l(); +}; + +#endif // defined HAVE_LAVC + +#endif // defined FF_CODEC_CONVERSIONS_TEST_H_277D34B0_7056_45BF_9A47_EA2AD1DEA846 From 0839bedc7e764cf827e2fa1331b32ea096cff8c5 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Mon, 21 Sep 2020 10:20:32 +0200 Subject: [PATCH 48/74] DeckLink: fixed one Coverity warning --- src/video_display/decklink.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/video_display/decklink.cpp b/src/video_display/decklink.cpp index 83e724973..08d18ef23 100644 --- a/src/video_display/decklink.cpp +++ b/src/video_display/decklink.cpp @@ -1741,7 +1741,9 @@ void HDRMetadata::Init(const string &fmt) { auto opts = unique_ptr<char []>(new char [fmt.size() + 1]); strcpy(opts.get(), fmt.c_str()); char *save_ptr = nullptr; - string mode = strtok_r(opts.get(), ",", &save_ptr); + char *mode_c = strtok_r(opts.get(), ",", &save_ptr); + assert(mode_c != nullptr); + string mode = mode_c; std::for_each(std::begin(mode), std::end(mode), [](char& c) { c = static_cast<char>(std::toupper(static_cast<unsigned char>(c))); }); From c8535466e506bfe0b2a33f71a3ad48a1acfd0f17 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Tue, 22 Sep 2020 08:28:01 +0200 Subject: [PATCH 49/74] Use rather sigdescr_np than sigabbrev_np This is more consistent with former sys_siglist --- src/main.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 3c68e51b1..d391fffc3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -272,14 +272,14 @@ static void crash_signal_handler(int sig) } #ifndef WIN32 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 32) - const char *sigabbrev = sigabbrev_np(sig); + const char *sig_desc = sigdescr_np(sig); #else - const char *sigabbrev = sys_siglist[sig]; + const char *sig_desc = sys_siglist[sig]; #endif - if (sigabbrev != NULL) { + if (sig_desc != NULL) { *ptr++ = ' '; *ptr++ = '('; - for (size_t i = 0; sigabbrev[i] != '\0'; ++i) { - *ptr++ = sigabbrev[i]; + for (size_t i = 0; sig_desc[i] != '\0'; ++i) { + *ptr++ = sig_desc[i]; } *ptr++ = ')'; } From 61071a0cbe0e54c664daa5bd1cf4ff085681ad56 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Tue, 22 Sep 2020 11:05:10 +0200 Subject: [PATCH 50/74] Libavcodec common: added 10-/12-bit YUV<->RGB conversions Use the 16-bit conversions also for 10 and 12 bit FFmpeg pixel formats. --- src/libavcodec_common.c | 171 +++++++++++++++++++++++++--------------- 1 file changed, 108 insertions(+), 63 deletions(-) diff --git a/src/libavcodec_common.c b/src/libavcodec_common.c index ce9eb0cc5..c25ec9e36 100644 --- a/src/libavcodec_common.c +++ b/src/libavcodec_common.c @@ -691,7 +691,10 @@ static void r10k_to_yuv422p10le(AVFrame * __restrict out_frame, unsigned char * } } -static void r10k_to_yuv444p16le(AVFrame * __restrict out_frame, unsigned char * __restrict in_data, int width, int height) +/** + * Converts to yuv444p 10/12/14 le + */ +static inline void r10k_to_yuv444pXXle(int depth, AVFrame * __restrict out_frame, unsigned char * __restrict in_data, int width, int height) { const int src_linesize = vc_get_linesize(width, R10k); for(int y = 0; y < height; y++) { @@ -704,28 +707,43 @@ static void r10k_to_yuv444p16le(AVFrame * __restrict out_frame, unsigned char * comp_type_t g = (src[1] & 0x3F ) << 4 | src[2] >> 4; comp_type_t b = (src[2] & 0x0F) << 6 | src[3] >> 2; - comp_type_t res_y = (RGB_TO_Y_709_SCALED(r, g, b) >> (COMP_BASE-6)) + (1<<12); - comp_type_t res_cb = (RGB_TO_CB_709_SCALED(r, g, b) >> (COMP_BASE-6)) + (1<<15); - comp_type_t res_cr = (RGB_TO_CR_709_SCALED(r, g, b) >> (COMP_BASE-6)) + (1<<15); + comp_type_t res_y = (RGB_TO_Y_709_SCALED(r, g, b) >> (COMP_BASE+10-depth)) + (1<<(depth-4)); + comp_type_t res_cb = (RGB_TO_CB_709_SCALED(r, g, b) >> (COMP_BASE+10-depth)) + (1<<(depth-1)); + comp_type_t res_cr = (RGB_TO_CR_709_SCALED(r, g, b) >> (COMP_BASE+10-depth)) + (1<<(depth-1)); - *dst_y++ = MIN(MAX(res_y, 1<<12), 235 * (1<<8)); - *dst_cb++ = MIN(MAX(res_cb, 1<<12), 240 * (1<<8)); - *dst_cr++ = MIN(MAX(res_cr, 1<<12), 240 * (1<<8)); + *dst_y++ = MIN(MAX(res_y, 1<<(depth-4)), 235 * (1<<(depth-8))); + *dst_cb++ = MIN(MAX(res_cb, 1<<(depth-4)), 240 * (1<<(depth-8))); + *dst_cr++ = MIN(MAX(res_cr, 1<<(depth-4)), 240 * (1<<(depth-8))); src += 4; } } } +static void r10k_to_yuv444p10le(AVFrame * __restrict out_frame, unsigned char * __restrict in_data, int width, int height) +{ + r10k_to_yuv444pXXle(10, out_frame, in_data, width, height); +} + +static void r10k_to_yuv444p12le(AVFrame * __restrict out_frame, unsigned char * __restrict in_data, int width, int height) +{ + r10k_to_yuv444pXXle(12, out_frame, in_data, width, height); +} + +static void r10k_to_yuv444p16le(AVFrame * __restrict out_frame, unsigned char * __restrict in_data, int width, int height) +{ + r10k_to_yuv444pXXle(16, out_frame, in_data, width, height); +} + // RGB full range to YCbCr bt. 709 limited range -static void r12l_to_yuv444p16le(AVFrame * __restrict out_frame, unsigned char * __restrict in_data, int width, int height) +static inline void r12l_to_yuv444pXXle(int depth, AVFrame * __restrict out_frame, unsigned char * __restrict in_data, int width, int height) { #define WRITE_RES \ - res_y = (RGB_TO_Y_709_SCALED(r, g, b) >> (COMP_BASE-4)) + (1<<12);\ - res_cb = (RGB_TO_CB_709_SCALED(r, g, b) >> (COMP_BASE-4)) + (1<<15);\ - res_cr = (RGB_TO_CR_709_SCALED(r, g, b) >> (COMP_BASE-4)) + (1<<15);\ - *dst_y++ = MIN(MAX(res_y, 1<<12), 235 * (1<<8));\ - *dst_cb++ = MIN(MAX(res_cb, 1<<12), 240 * (1<<8));\ - *dst_cr++ = MIN(MAX(res_cr, 1<<12), 240 * (1<<8)); + res_y = (RGB_TO_Y_709_SCALED(r, g, b) >> (COMP_BASE+12-depth)) + (1<<(depth-4));\ + res_cb = (RGB_TO_CB_709_SCALED(r, g, b) >> (COMP_BASE+12-depth)) + (1<<(depth-1));\ + res_cr = (RGB_TO_CR_709_SCALED(r, g, b) >> (COMP_BASE+12-depth)) + (1<<(depth-1));\ + *dst_y++ = MIN(MAX(res_y, 1<<(depth-4)), 235 * (1<<(depth-8)));\ + *dst_cb++ = MIN(MAX(res_cb, 1<<(depth-4)), 240 * (1<<(depth-8)));\ + *dst_cr++ = MIN(MAX(res_cr, 1<<(depth-4)), 240 * (1<<(depth-8))); const int src_linesize = vc_get_linesize(width, R12L); for (int y = 0; y < height; ++y) { @@ -810,6 +828,21 @@ static void r12l_to_yuv444p16le(AVFrame * __restrict out_frame, unsigned char * } } +static void r12l_to_yuv444p10le(AVFrame * __restrict out_frame, unsigned char * __restrict in_data, int width, int height) +{ + r12l_to_yuv444pXXle(10, out_frame, in_data, width, height); +} + +static void r12l_to_yuv444p12le(AVFrame * __restrict out_frame, unsigned char * __restrict in_data, int width, int height) +{ + r12l_to_yuv444pXXle(12, out_frame, in_data, width, height); +} + +static void r12l_to_yuv444p16le(AVFrame * __restrict out_frame, unsigned char * __restrict in_data, int width, int height) +{ + r12l_to_yuv444pXXle(16, out_frame, in_data, width, height); +} + static void rgb_to_bgr0(AVFrame * __restrict out_frame, unsigned char * __restrict in_data, int width, int height) { int src_linesize = vc_get_linesize(width, RGB); @@ -1067,7 +1100,7 @@ static void gbrp10le_to_r10k(char * __restrict dst_buffer, AVFrame * __restrict } } -static void yuv444p16le_to_r10k(char * __restrict dst_buffer, AVFrame * __restrict frame, +static void yuv444pXXle_to_r10k(int depth, char * __restrict dst_buffer, AVFrame * __restrict frame, int width, int height, int pitch, int * __restrict rgb_shift) { UNUSED(rgb_shift); @@ -1078,13 +1111,13 @@ static void yuv444p16le_to_r10k(char * __restrict dst_buffer, AVFrame * __restri unsigned char *dst = (unsigned char *) dst_buffer + y * pitch; OPTIMIZED_FOR (int x = 0; x < width; ++x) { - comp_type_t y = (y_scale * (*src_y++ - (1<<12))); - comp_type_t cr = *src_cr++ - (1<<15); - comp_type_t cb = *src_cb++ - (1<<15); + comp_type_t y = (y_scale * (*src_y++ - (1<<(depth-4)))); + comp_type_t cr = *src_cr++ - (1<<(depth-1)); + comp_type_t cb = *src_cb++ - (1<<(depth-1)); - comp_type_t r = YCBCR_TO_R_709_SCALED(y, cb, cr) >> (COMP_BASE+6); - comp_type_t g = YCBCR_TO_G_709_SCALED(y, cb, cr) >> (COMP_BASE+6); - comp_type_t b = YCBCR_TO_B_709_SCALED(y, cb, cr) >> (COMP_BASE+6); + comp_type_t r = YCBCR_TO_R_709_SCALED(y, cb, cr) >> (COMP_BASE-10+depth); + comp_type_t g = YCBCR_TO_G_709_SCALED(y, cb, cr) >> (COMP_BASE-10+depth); + comp_type_t b = YCBCR_TO_B_709_SCALED(y, cb, cr) >> (COMP_BASE-10+depth); // r g b is now on 10 bit scale r = MIN((255<<2) - 1, MAX(1<<2, r)); @@ -1099,7 +1132,25 @@ static void yuv444p16le_to_r10k(char * __restrict dst_buffer, AVFrame * __restri } } -static void yuv444p16le_to_r12l(char * __restrict dst_buffer, AVFrame * __restrict frame, +static void yuv444p10le_to_r10k(char * __restrict dst_buffer, AVFrame * __restrict frame, + int width, int height, int pitch, int * __restrict rgb_shift) +{ + yuv444pXXle_to_r10k(10, dst_buffer, frame, width, height, pitch, rgb_shift); +} + +static void yuv444p12le_to_r10k(char * __restrict dst_buffer, AVFrame * __restrict frame, + int width, int height, int pitch, int * __restrict rgb_shift) +{ + yuv444pXXle_to_r10k(12, dst_buffer, frame, width, height, pitch, rgb_shift); +} + +static void yuv444p16le_to_r10k(char * __restrict dst_buffer, AVFrame * __restrict frame, + int width, int height, int pitch, int * __restrict rgb_shift) +{ + yuv444pXXle_to_r10k(16, dst_buffer, frame, width, height, pitch, rgb_shift); +} + +static void yuv444pXXle_to_r12l(int depth, char * __restrict dst_buffer, AVFrame * __restrict frame, int width, int height, int pitch, int * __restrict rgb_shift) { UNUSED(rgb_shift); @@ -1114,12 +1165,12 @@ static void yuv444p16le_to_r12l(char * __restrict dst_buffer, AVFrame * __restri comp_type_t g[8]; comp_type_t b[8]; OPTIMIZED_FOR (int j = 0; j < 8; ++j) { - comp_type_t y = (y_scale * (*src_y++ - (1<<12))); - comp_type_t cr = *src_cr++ - (1<<15); - comp_type_t cb = *src_cb++ - (1<<15); - comp_type_t rr = YCBCR_TO_R_709_SCALED(y, cb, cr) >> (COMP_BASE+4); - comp_type_t gg = YCBCR_TO_G_709_SCALED(y, cb, cr) >> (COMP_BASE+4); - comp_type_t bb = YCBCR_TO_B_709_SCALED(y, cb, cr) >> (COMP_BASE+4); + comp_type_t y = (y_scale * (*src_y++ - (1<<(depth-4)))); + comp_type_t cr = *src_cr++ - (1<<(depth-1)); + comp_type_t cb = *src_cb++ - (1<<(depth-1)); + comp_type_t rr = YCBCR_TO_R_709_SCALED(y, cb, cr) >> (COMP_BASE-12+depth); + comp_type_t gg = YCBCR_TO_G_709_SCALED(y, cb, cr) >> (COMP_BASE-12+depth); + comp_type_t bb = YCBCR_TO_B_709_SCALED(y, cb, cr) >> (COMP_BASE-12+depth); r[j] = MIN((255<<4) - 1, MAX(1<<4, rr)); g[j] = MIN((255<<4) - 1, MAX(1<<4, gg)); b[j] = MIN((255<<4) - 1, MAX(1<<4, bb)); @@ -1166,6 +1217,24 @@ static void yuv444p16le_to_r12l(char * __restrict dst_buffer, AVFrame * __restri } } +static void yuv444p10le_to_r12l(char * __restrict dst_buffer, AVFrame * __restrict frame, + int width, int height, int pitch, int * __restrict rgb_shift) +{ + yuv444pXXle_to_r12l(10, dst_buffer, frame, width, height, pitch, rgb_shift); +} + +static void yuv444p12le_to_r12l(char * __restrict dst_buffer, AVFrame * __restrict frame, + int width, int height, int pitch, int * __restrict rgb_shift) +{ + yuv444pXXle_to_r12l(12, dst_buffer, frame, width, height, pitch, rgb_shift); +} + +static void yuv444p16le_to_r12l(char * __restrict dst_buffer, AVFrame * __restrict frame, + int width, int height, int pitch, int * __restrict rgb_shift) +{ + yuv444pXXle_to_r12l(16, dst_buffer, frame, width, height, pitch, rgb_shift); +} + static void gbrp10le_to_rgb(char * __restrict dst_buffer, AVFrame * __restrict frame, int width, int height, int pitch, int * __restrict rgb_shift) { @@ -2116,39 +2185,6 @@ static inline void yuv422p10le_to_rgb32(char * __restrict dst_buffer, AVFrame * yuv422p10le_to_rgb(dst_buffer, in_frame, width, height, pitch, rgb_shift, true); } -/// @todo this is almost identical to yuv444p16le_to_r10k except constants, merge -static void yuv444p10le_to_r10k(char * __restrict dst_buffer, AVFrame * __restrict frame, - int width, int height, int pitch, int * __restrict rgb_shift) -{ - UNUSED(rgb_shift); - for (int y = 0; y < height; ++y) { - uint16_t *src_y = (uint16_t *) (frame->data[0] + frame->linesize[0] * y); - uint16_t *src_cb = (uint16_t *) (frame->data[1] + frame->linesize[1] * y); - uint16_t *src_cr = (uint16_t *) (frame->data[2] + frame->linesize[2] * y); - unsigned char *dst = (unsigned char *) dst_buffer + y * pitch; - - OPTIMIZED_FOR (int x = 0; x < width; ++x) { - comp_type_t y = (y_scale * (*src_y++ - (1<<6))); - comp_type_t cr = *src_cr++ - (1<<9); - comp_type_t cb = *src_cb++ - (1<<9); - - comp_type_t r = YCBCR_TO_R_709_SCALED(y, cb, cr) >> COMP_BASE; - comp_type_t g = YCBCR_TO_G_709_SCALED(y, cb, cr) >> COMP_BASE; - comp_type_t b = YCBCR_TO_B_709_SCALED(y, cb, cr) >> COMP_BASE; - // r g b is now on 10 bit scale - - r = MIN((255<<2) - 1, MAX(1<<2, r)); - g = MIN((255<<2) - 1, MAX(1<<2, g)); - b = MIN((255<<2) - 1, MAX(1<<2, b)); - - *dst++ = r >> 2; - *dst++ = (r & 0x3) << 6 | g >> 4; - *dst++ = (g & 0xF) << 4 | b >> 6; - *dst++ = (b & 0x3F) << 2; - } - } -} - static inline void yuv444p10le_to_rgb24(char * __restrict dst_buffer, AVFrame * __restrict in_frame, int width, int height, int pitch, int * __restrict rgb_shift) { @@ -2330,7 +2366,11 @@ const struct uv_to_av_conversion *get_uv_to_av_conversions() { { v210, AV_PIX_FMT_YUV422P10LE, v210_to_yuv422p10le }, { v210, AV_PIX_FMT_YUV444P10LE, v210_to_yuv444p10le }, { v210, AV_PIX_FMT_YUV444P16LE, v210_to_yuv444p16le }, + { R10k, AV_PIX_FMT_YUV444P10LE, r10k_to_yuv444p10le }, + { R10k, AV_PIX_FMT_YUV444P12LE, r10k_to_yuv444p12le }, { R10k, AV_PIX_FMT_YUV444P16LE, r10k_to_yuv444p16le }, + { R12L, AV_PIX_FMT_YUV444P16LE, r12l_to_yuv444p10le }, + { R12L, AV_PIX_FMT_YUV444P16LE, r12l_to_yuv444p12le }, { R12L, AV_PIX_FMT_YUV444P16LE, r12l_to_yuv444p16le }, #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(55, 15, 100) // FFMPEG commit c2869b4640f { v210, AV_PIX_FMT_P010LE, v210_to_p010le }, @@ -2388,6 +2428,7 @@ const struct av_to_uv_conversion *get_av_to_uv_conversions() { {AV_PIX_FMT_YUV444P10LE, R10k, yuv444p10le_to_r10k, false}, {AV_PIX_FMT_YUV444P10LE, RGB, yuv444p10le_to_rgb24, false}, {AV_PIX_FMT_YUV444P10LE, RGBA, yuv444p10le_to_rgb32, false}, + {AV_PIX_FMT_YUV444P16LE, R12L, yuv444p10le_to_r12l, false}, #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(55, 15, 100) // FFMPEG commit c2869b4640f {AV_PIX_FMT_P010LE, v210, p010le_to_v210, true}, {AV_PIX_FMT_P010LE, UYVY, p010le_to_uyvy, true}, @@ -2422,6 +2463,12 @@ const struct av_to_uv_conversion *get_av_to_uv_conversions() { {AV_PIX_FMT_NV12, UYVY, nv12_to_uyvy, true}, {AV_PIX_FMT_NV12, RGB, nv12_to_rgb24, false}, {AV_PIX_FMT_NV12, RGB, nv12_to_rgb32, false}, + // 12-bit YUV + {AV_PIX_FMT_YUV444P12LE, R10k, yuv444p12le_to_r10k, false}, + {AV_PIX_FMT_YUV444P16LE, R12L, yuv444p12le_to_r12l, false}, + // 16-bit YUV + {AV_PIX_FMT_YUV444P16LE, R10k, yuv444p16le_to_r10k, false}, + {AV_PIX_FMT_YUV444P16LE, R12L, yuv444p16le_to_r12l, false}, // RGB {AV_PIX_FMT_GBRP, RGB, gbrp_to_rgb, true}, {AV_PIX_FMT_GBRP, RGBA, gbrp_to_rgba, true}, @@ -2429,8 +2476,6 @@ const struct av_to_uv_conversion *get_av_to_uv_conversions() { {AV_PIX_FMT_RGB24, RGB, memcpy_data, true}, {AV_PIX_FMT_RGB24, RGBA, rgb24_to_rgb32, false}, {AV_PIX_FMT_GBRP10LE, R10k, gbrp10le_to_r10k, true}, - {AV_PIX_FMT_YUV444P16LE, R10k, yuv444p16le_to_r10k, false}, - {AV_PIX_FMT_YUV444P16LE, R12L, yuv444p16le_to_r12l, false}, {AV_PIX_FMT_GBRP10LE, RGB, gbrp10le_to_rgb, false}, {AV_PIX_FMT_GBRP10LE, RGBA, gbrp10le_to_rgba, false}, #ifdef HAVE_12_AND_14_PLANAR_COLORSPACES From 5086d08dee0d3443e7087aeb84439f316e660d06 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Tue, 22 Sep 2020 15:53:43 +0200 Subject: [PATCH 51/74] Lavc: default to 420 only for I420 Before that, 420 was used for every non-interlaced video (even v210). Of course if available, which was not the case for RGB pixel formats. --- src/video_codec.c | 10 ++++++++++ src/video_codec.h | 1 + src/video_compress/libavcodec.cpp | 6 +++--- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/video_codec.c b/src/video_codec.c index 99c49a345..08b3eec51 100644 --- a/src/video_codec.c +++ b/src/video_codec.c @@ -2542,4 +2542,14 @@ void codec_get_planes_subsampling(codec_t pix_fmt, int *sub) { } } +bool codec_is_420(codec_t pix_fmt) +{ + return pixfmt_plane_info[pix_fmt].plane_info[0] == 1 && + pixfmt_plane_info[pix_fmt].plane_info[1] == 1 && + pixfmt_plane_info[pix_fmt].plane_info[2] == 2 && + pixfmt_plane_info[pix_fmt].plane_info[3] == 2 && + pixfmt_plane_info[pix_fmt].plane_info[4] == 2 && + pixfmt_plane_info[pix_fmt].plane_info[5] == 2; +} + /* vim: set expandtab sw=8: */ diff --git a/src/video_codec.h b/src/video_codec.h index 7dba01e9e..5f76c9b5a 100644 --- a/src/video_codec.h +++ b/src/video_codec.h @@ -96,6 +96,7 @@ int get_pf_block_size(codec_t codec) ATTRIBUTE(const); int vc_get_linesize(unsigned int width, codec_t codec) ATTRIBUTE(const); size_t vc_get_datalen(unsigned int width, unsigned int height, codec_t codec) ATTRIBUTE(const); void codec_get_planes_subsampling(codec_t pix_fmt, int *sub); +bool codec_is_420(codec_t pix_fmt); int codec_is_a_rgb(codec_t codec) ATTRIBUTE(const); bool codec_is_in_set(codec_t codec, codec_t *set) ATTRIBUTE(const); int codec_is_const_size(codec_t codec) ATTRIBUTE(const); diff --git a/src/video_compress/libavcodec.cpp b/src/video_compress/libavcodec.cpp index ddd23140e..446fb9a31 100644 --- a/src/video_compress/libavcodec.cpp +++ b/src/video_compress/libavcodec.cpp @@ -841,10 +841,10 @@ static list<enum AVPixelFormat> get_available_pix_fmts(struct video_desc in_desc bool is_rgb = codec_is_a_rgb(in_desc.color_spec); int preferred_subsampling = requested_subsampling; if (requested_subsampling == 0) { - if (in_desc.interlacing == INTERLACED_MERGED) { - preferred_subsampling = 422; - } else { + if (codec_is_420(in_desc.color_spec)) { /// @todo perhaps better would be take the subs. directly preferred_subsampling = 420; + } else { + preferred_subsampling = 422; } } // sort From 9b37b99c3744dd6d9456b5a4aa027466ddf81d12 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Wed, 23 Sep 2020 16:00:42 +0200 Subject: [PATCH 52/74] Screen macOS: option to specify display --- src/video_capture/screen_osx.c | 81 +++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 10 deletions(-) diff --git a/src/video_capture/screen_osx.c b/src/video_capture/screen_osx.c index 50b09cdf2..9e7fd6251 100644 --- a/src/video_capture/screen_osx.c +++ b/src/video_capture/screen_osx.c @@ -50,6 +50,7 @@ #include "tv.h" +#include "audio/audio.h" #include "audio/audio.h" #include <stdio.h> @@ -60,8 +61,10 @@ #include <Carbon/Carbon.h> +#define MAX_DISPLAY_COUNT 10 #define MOD_NAME "[screen cap mac] " + /* prototypes of functions defined in this module */ static void show_help(void); static void vidcap_screen_osx_done(void *state); @@ -70,9 +73,24 @@ static void show_help() { printf("Screen capture\n"); printf("Usage\n"); - printf("\t-t screen[:fps=<fps>][:codec=<c>]\n"); + printf("\t-t screen[:fps=<fps>][:codec=<c>][:display=<d>]\n"); printf("\t\t<fps> - preferred grabbing fps (otherwise unlimited)\n"); printf("\t\t <c> - requested codec to capture (RGB /default/ or RGBA)\n"); + printf("\t\t <d> - display ID or \"primary\" or \"secondary\"\n"); + printf("\n\nAvailable displays:\n"); + + CGDirectDisplayID screens[MAX_DISPLAY_COUNT]; + uint32_t count = 0; + CGGetOnlineDisplayList(sizeof screens / sizeof screens[0], screens, &count); + + for (unsigned int i = 0; i < count; ++i) { + char flags[128]; + strcpy(flags, CGDisplayIsMain(screens[i]) ? "primary" : "secondary"); + if (CGDisplayIsBuiltin(screens[i])) { + strncat(flags, ", builtin", sizeof flags - strlen(flags) - 1); + } + printf("\tID %u) %s\n", screens[i], flags); + } } struct vidcap_screen_osx_state { @@ -88,14 +106,19 @@ struct vidcap_screen_osx_state { bool initialized; }; -static void initialize(struct vidcap_screen_osx_state *s) { - s->display = CGMainDisplayID(); +static bool initialize(struct vidcap_screen_osx_state *s) { CGImageRef image = CGDisplayCreateImage(s->display); + if (image == NULL) { + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable create image (wrong display ID?)\n"); + return false; + } s->desc.width = CGImageGetWidth(image); s->desc.height = CGImageGetHeight(image); CFRelease(image); s->video_frame_pool = video_frame_pool_init(s->desc, 2); + + return true; } static struct vidcap_type * vidcap_screen_osx_probe(bool verbose, void (**deleter)(void *)) @@ -161,14 +184,50 @@ static int vidcap_screen_osx_init(struct vidcap_params *params, void **state) s->desc.fps = 30; s->desc.interlacing = PROGRESSIVE; - if(vidcap_params_get_fmt(params)) { + s->display = CGMainDisplayID(); + + if (vidcap_params_get_fmt(params) && strlen(vidcap_params_get_fmt(params)) > 0) { if (strcmp(vidcap_params_get_fmt(params), "help") == 0) { show_help(); return VIDCAP_INIT_NOERR; - } else if (strncasecmp(vidcap_params_get_fmt(params), "fps=", strlen("fps=")) == 0) { - s->desc.fps = atof(vidcap_params_get_fmt(params) + strlen("fps=")); - } else if (strncasecmp(vidcap_params_get_fmt(params), "codec=", strlen("codec=")) == 0) { - s->desc.color_spec = get_codec_from_name(vidcap_params_get_fmt(params) + strlen("codec=")); + } + char *fmt = alloca(strlen(vidcap_params_get_fmt(params) + 1)); + strcpy(fmt, vidcap_params_get_fmt(params)); + char *save_ptr = NULL; + char *item = NULL; + while ((item = strtok_r(fmt, ":", &save_ptr)) != NULL) { + if (strncasecmp(item, "fps=", strlen("fps=")) == 0) { + s->desc.fps = atof(item + strlen("fps=")); + } else if (strncasecmp(item, "codec=", strlen("codec=")) == 0) { + s->desc.color_spec = get_codec_from_name(item + strlen("codec=")); + } else if (strncasecmp(item, "display=", strlen("display=")) == 0) { + char *display = item + strlen("display="); + + if (strcasecmp(display, "secondary") == 0) { + CGDirectDisplayID screens[MAX_DISPLAY_COUNT]; + uint32_t count = 0; + CGGetOnlineDisplayList(sizeof screens / sizeof screens[0], screens, &count); + uint32_t i = 0; + for (; i < count; ++i) { + if (!CGDisplayIsMain(screens[i])) { + s->display = screens[i]; + break; + } + } + if (i == count) { + log_msg(LOG_LEVEL_ERROR, MOD_NAME "No secondary scren found!\n"); + vidcap_screen_osx_done(s); + return VIDCAP_INIT_FAIL; + } + } if (strcasecmp(display, "primary") != 0) { // primary was already set + s->display = atol(display); + } + } else { + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unrecognized option \"%s\"\n", item); + vidcap_screen_osx_done(s); + return VIDCAP_INIT_FAIL; + } + fmt = NULL; } } @@ -205,8 +264,10 @@ static struct video_frame * vidcap_screen_osx_grab(void *state, struct audio_fra struct vidcap_screen_osx_state *s = (struct vidcap_screen_osx_state *) state; if (!s->initialized) { - initialize(s); - s->initialized = true; + s->initialized = initialize(s); + if (!s->initialized) { + return NULL; + } } struct video_frame *frame = video_frame_pool_get_disposable_frame(s->video_frame_pool); From d7d6698c2d53489d64616cf9a73f8bf35364cdae Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Wed, 23 Sep 2020 13:40:34 +0200 Subject: [PATCH 53/74] Lavd: print also pixfmt change duration in debug --- src/video_decompress/libavcodec.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/video_decompress/libavcodec.c b/src/video_decompress/libavcodec.c index 12b0d7031..47985b804 100644 --- a/src/video_decompress/libavcodec.c +++ b/src/video_decompress/libavcodec.c @@ -607,6 +607,7 @@ static bool lavd_sws_convert(struct state_libavcodec_decompress_sws *sws, enum A static int change_pixfmt(AVFrame *frame, unsigned char *dst, int av_codec, codec_t out_codec, int width, int height, int pitch, int rgb_shift[static restrict 3], struct state_libavcodec_decompress_sws *sws) { av_to_uv_convert_p convert = NULL; + for (const struct av_to_uv_conversion *c = get_av_to_uv_conversions(); c->uv_codec != VIDEO_CODEC_NONE; c++) { if (c->av_codec == av_codec && c->uv_codec == out_codec) { convert = c->convert; @@ -724,7 +725,8 @@ static decompress_status libavcodec_decompress(void *state, unsigned char *dst, } if(got_frame) { - log_msg(LOG_LEVEL_DEBUG, "[lavd] Decompressing %c frame took %f sec.\n", av_get_picture_type_char(s->frame->pict_type), tv_diff(t1, t0)); + struct timeval t3; + gettimeofday(&t3, NULL); s->frame->opaque = callbacks; /* Skip the frame if this is not an I-frame @@ -758,6 +760,9 @@ static decompress_status libavcodec_decompress(void *state, unsigned char *dst, res = DECODER_GOT_FRAME; } } + struct timeval t4; + gettimeofday(&t4, NULL); + log_msg(LOG_LEVEL_DEBUG, MOD_NAME "Decompressing %c frame took %f sec, pixfmt change %f s.\n", av_get_picture_type_char(s->frame->pict_type), tv_diff(t1, t0), tv_diff(t4, t3)); } if (len <= 0) { From d34ca8c072bad1339c5b9eeb7f2060ba48444535 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Wed, 23 Sep 2020 13:42:08 +0200 Subject: [PATCH 54/74] Libavcodec common.: check comp_type_t size instead int_fast32_t --- src/libavcodec_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libavcodec_common.c b/src/libavcodec_common.c index c25ec9e36..939d1c7d6 100644 --- a/src/libavcodec_common.c +++ b/src/libavcodec_common.c @@ -204,8 +204,8 @@ bool libav_codec_has_extradata(codec_t codec) { * Use this transformations in all conversions. * @{ */ -#define COMP_BASE (sizeof(int_fast32_t) == 4 ? 15 : 18) // computation will be less precise when int_fast32_t is 32 bit typedef int_fast32_t comp_type_t; +#define COMP_BASE (sizeof(comp_type_t) == 4 ? 15 : 18) // computation will be less precise when int_fast32_t is 32 bit static_assert(sizeof(comp_type_t) * 8 >= COMP_BASE + 17, "comp_type_t not wide enough (we are computing in up to 16 bits!)"); static const comp_type_t y_r = (0.2126*219/255) * (1<<COMP_BASE); static const comp_type_t y_g = (0.7152*219/255) * (1<<COMP_BASE); From 907a4f806de9f6db3d9a597f6c8474ea1de422dd Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Thu, 24 Sep 2020 13:44:10 +0200 Subject: [PATCH 55/74] Tests FF conversions: optionaly measure performance --- test/ff_codec_conversions_test.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/ff_codec_conversions_test.cpp b/test/ff_codec_conversions_test.cpp index 8754b7de8..289bc4e11 100644 --- a/test/ff_codec_conversions_test.cpp +++ b/test/ff_codec_conversions_test.cpp @@ -9,17 +9,20 @@ #include <algorithm> #include <array> #include <cppunit/config/SourcePrefix.h> +#include <iostream> #include <random> #include <string> #include <vector> #include "ff_codec_conversions_test.h" #include "libavcodec_common.h" +#include "tv.h" #include "video_capture/testcard_common.h" #include "video_codec.h" using std::array; using std::copy; +using std::cout; using std::default_random_engine; using std::max; using std::to_string; @@ -46,6 +49,7 @@ ff_codec_conversions_test::tearDown() { } +#define TIMER(t) struct timeval t{}; gettimeofday(&(t), nullptr) void ff_codec_conversions_test::test_yuv444p16le_from_to_r10k() { @@ -75,8 +79,15 @@ ff_codec_conversions_test::test_yuv444p16le_from_to_r10k() auto to_conv = get_av_to_uv_conversion(frame.format, R10k); assert(to_conv != nullptr && from_conv != nullptr); + TIMER(t0); from_conv(&frame, r10k_buf.data(), width, height); + TIMER(t1); to_conv(reinterpret_cast<char*>(r10k_buf.data()), &frame, width, height, vc_get_linesize(width, R10k), nullptr); + TIMER(t2); + + if (getenv("PERF") != nullptr) { + cout << "test_yuv444p16le_from_to_r10k: duration - enc " << tv_diff(t1, t0) << ", dec " <<tv_diff(t2, t1) << "\n"; + } av_freep(frame.data); @@ -143,8 +154,15 @@ ff_codec_conversions_test::test_yuv444p16le_from_to_r12l() auto to_conv = get_av_to_uv_conversion(frame.format, R12L); assert(to_conv != nullptr && from_conv != nullptr); + TIMER(t0); from_conv(&frame, r12l_buf.data(), width, height); + TIMER(t1); to_conv(reinterpret_cast<char*>(r12l_buf.data()), &frame, width, height, vc_get_linesize(width, R12L), nullptr); + TIMER(t2); + + if (getenv("PERF") != nullptr) { + cout << "test_yuv444p16le_from_to_r12l: duration - enc " << tv_diff(t1, t0) << ", dec " <<tv_diff(t2, t1) << "\n"; + } av_freep(frame.data); From adbf54d7a50482c62fcb5c5d292d7f0e049973ad Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Thu, 24 Sep 2020 14:27:40 +0200 Subject: [PATCH 56/74] Lavc: use int32_t instead of int_fast32_t int32_t is faster than use of int_fast32_t --- src/libavcodec_common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libavcodec_common.c b/src/libavcodec_common.c index 939d1c7d6..bae09c4dc 100644 --- a/src/libavcodec_common.c +++ b/src/libavcodec_common.c @@ -204,8 +204,8 @@ bool libav_codec_has_extradata(codec_t codec) { * Use this transformations in all conversions. * @{ */ -typedef int_fast32_t comp_type_t; -#define COMP_BASE (sizeof(comp_type_t) == 4 ? 15 : 18) // computation will be less precise when int_fast32_t is 32 bit +typedef int32_t comp_type_t; // int32_t provides much better performance than int_fast32_t +#define COMP_BASE (sizeof(comp_type_t) == 4 ? 15 : 18) // computation will be less precise when comp_type_t is 32 bit static_assert(sizeof(comp_type_t) * 8 >= COMP_BASE + 17, "comp_type_t not wide enough (we are computing in up to 16 bits!)"); static const comp_type_t y_r = (0.2126*219/255) * (1<<COMP_BASE); static const comp_type_t y_g = (0.7152*219/255) * (1<<COMP_BASE); From 518a67b470286d749efca65d55ea479ddda7257a Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Thu, 24 Sep 2020 14:27:49 +0200 Subject: [PATCH 57/74] Rewrite yuv444p10le_to_rgb24/32 Rewrite yuv444p10le_to_rgb24/32 to use standard conversions and use common code. --- src/libavcodec_common.c | 52 ++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/src/libavcodec_common.c b/src/libavcodec_common.c index bae09c4dc..81b1a8330 100644 --- a/src/libavcodec_common.c +++ b/src/libavcodec_common.c @@ -2185,10 +2185,9 @@ static inline void yuv422p10le_to_rgb32(char * __restrict dst_buffer, AVFrame * yuv422p10le_to_rgb(dst_buffer, in_frame, width, height, pitch, rgb_shift, true); } -static inline void yuv444p10le_to_rgb24(char * __restrict dst_buffer, AVFrame * __restrict in_frame, - int width, int height, int pitch, int * __restrict rgb_shift) +static inline void yuv444p10le_to_rgb(char * __restrict dst_buffer, AVFrame * __restrict in_frame, + int width, int height, int pitch, int * __restrict rgb_shift, bool rgba) { - UNUSED(rgb_shift); for (int y = 0; y < height; y++) { uint16_t *src_y = (uint16_t *)(void *)(in_frame->data[0] + in_frame->linesize[0] * y); uint16_t *src_cb = (uint16_t *)(void *)(in_frame->data[1] + in_frame->linesize[1] * y); @@ -2196,39 +2195,34 @@ static inline void yuv444p10le_to_rgb24(char * __restrict dst_buffer, AVFrame * uint8_t *dst = (uint8_t *)(void *)(dst_buffer + y * pitch); OPTIMIZED_FOR (int x = 0; x < width; ++x) { - int cb = (*src_cb++ >> 2) - 128; - int cr = (*src_cr++ >> 2) - 128; - int y = (*src_y++ >> 2) << 16; - int r = 75700 * cr; - int g = -26864 * cb - 38050 * cr; - int b = 133176 * cb; - *dst++ = MIN(MAX(r + y, 0), (1<<24) - 1) >> 16; - *dst++ = MIN(MAX(g + y, 0), (1<<24) - 1) >> 16; - *dst++ = MIN(MAX(b + y, 0), (1<<24) - 1) >> 16; + comp_type_t cb = (*src_cb++ >> 2) - 128; + comp_type_t cr = (*src_cr++ >> 2) - 128; + comp_type_t y = (*src_y++ >> 2) * y_scale; + comp_type_t r = r_cr * cr; + comp_type_t g = g_cb * cb + g_cr * cr; + comp_type_t b = b_cb * cb; + if (rgba) { + *(uint32_t *)(void *) dst = (MIN(MAX(r + y, 0), (1<<24) - 1) >> COMP_BASE) << rgb_shift[0] | (MIN(MAX(g + y, 0), (1<<24) - 1) >> COMP_BASE) << rgb_shift[1] | + (MIN(MAX(b + y, 0), (1<<24) - 1) >> COMP_BASE) << rgb_shift[2]; + } else { + *dst++ = MIN(MAX((r + y) >> (COMP_BASE), 0), (1<<8) - 1); + *dst++ = MIN(MAX((g + y) >> (COMP_BASE), 0), (1<<8) - 1); + *dst++ = MIN(MAX((b + y) >> (COMP_BASE), 0), (1<<8) - 1); + } } } } +static inline void yuv444p10le_to_rgb24(char * __restrict dst_buffer, AVFrame * __restrict in_frame, + int width, int height, int pitch, int * __restrict rgb_shift) +{ + yuv444p10le_to_rgb(dst_buffer, in_frame, width, height, pitch, rgb_shift, false); +} + static inline void yuv444p10le_to_rgb32(char * __restrict dst_buffer, AVFrame * __restrict in_frame, int width, int height, int pitch, int * __restrict rgb_shift) { - for (int y = 0; y < height; y++) { - uint16_t *src_y = (uint16_t *)(void *)(in_frame->data[0] + in_frame->linesize[0] * y); - uint16_t *src_cb = (uint16_t *)(void *)(in_frame->data[1] + in_frame->linesize[1] * y); - uint16_t *src_cr = (uint16_t *)(void *)(in_frame->data[2] + in_frame->linesize[2] * y); - uint32_t *dst = (uint32_t *)(void *)(dst_buffer + y * pitch); - - OPTIMIZED_FOR (int x = 0; x < width; ++x) { - int cb = (*src_cb++ >> 2) - 128; - int cr = (*src_cr++ >> 2) - 128; - int y = (*src_y++ >> 2) << 16; - int r = 75700 * cr; - int g = -26864 * cb - 38050 * cr; - int b = 133176 * cb; - *dst++ = (MIN(MAX(r + y, 0), (1<<24) - 1) >> 16) << rgb_shift[0] | (MIN(MAX(g + y, 0), (1<<24) - 1) >> 16) << rgb_shift[1] | - (MIN(MAX(b + y, 0), (1<<24) - 1) >> 16) << rgb_shift[2]; - } - } + yuv444p10le_to_rgb(dst_buffer, in_frame, width, height, pitch, rgb_shift, true); } static void p010le_to_v210(char * __restrict dst_buffer, AVFrame * __restrict in_frame, From 9b1fa96370e9a4c819b31891deb306d82e93909e Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Thu, 24 Sep 2020 16:03:22 +0200 Subject: [PATCH 58/74] Libavcodec common: fixed YUV8->RGB(a) conversions * use common coefficients (there was color shift before) * utilize RGB-shifts for RGBA --- src/libavcodec_common.c | 170 +++++++++++++++++----------------------- 1 file changed, 74 insertions(+), 96 deletions(-) diff --git a/src/libavcodec_common.c b/src/libavcodec_common.c index 81b1a8330..3383cbc6f 100644 --- a/src/libavcodec_common.c +++ b/src/libavcodec_common.c @@ -1711,105 +1711,67 @@ static void nv12_to_rgb32(char * __restrict dst_buffer, AVFrame * __restrict in_ } /** - * Changes pixel format from planar YUV 422 to packed RGB/A. + * Changes pixel format from planar 8-bit YUV to packed RGB/A. * Color space is assumed ITU-T Rec. 609. YUV is expected to be full scale (aka in JPEG). */ -static inline void yuv422p_to_rgb(char * __restrict dst_buffer, AVFrame * __restrict in_frame, - int width, int height, int pitch, int * __restrict rgb_shift, bool rgba) -{ - UNUSED(rgb_shift); - for(int y = 0; y < height; ++y) { - unsigned char *src_y = (unsigned char *) in_frame->data[0] + in_frame->linesize[0] * y; - unsigned char *src_cb = (unsigned char *) in_frame->data[1] + in_frame->linesize[1] * y; - unsigned char *src_cr = (unsigned char *) in_frame->data[2] + in_frame->linesize[2] * y; - unsigned char *dst = (unsigned char *) dst_buffer + pitch * y; - - OPTIMIZED_FOR (int x = 0; x < width / 2; ++x) { - int cb = *src_cb++ - 128; - int cr = *src_cr++ - 128; - int y = *src_y++ << 16; - int r = 75700 * cr; - int g = -26864 * cb - 38050 * cr; - int b = 133176 * cb; - *dst++ = MIN(MAX(r + y, 0), (1<<24) - 1) >> 16; - *dst++ = MIN(MAX(g + y, 0), (1<<24) - 1) >> 16; - *dst++ = MIN(MAX(b + y, 0), (1<<24) - 1) >> 16; - if (rgba) { - *dst++ = 255; - } - y = *src_y++ << 16; - *dst++ = MIN(MAX(r + y, 0), (1<<24) - 1) >> 16; - *dst++ = MIN(MAX(g + y, 0), (1<<24) - 1) >> 16; - *dst++ = MIN(MAX(b + y, 0), (1<<24) - 1) >> 16; - if (rgba) { - *dst++ = 255; - } - } - } -} - -static void yuv422p_to_rgb24(char * __restrict dst_buffer, AVFrame * __restrict in_frame, - int width, int height, int pitch, int * __restrict rgb_shift) -{ - yuv422p_to_rgb(dst_buffer, in_frame, width, height, pitch, rgb_shift, false); -} - -static void yuv422p_to_rgb32(char * __restrict dst_buffer, AVFrame * __restrict in_frame, - int width, int height, int pitch, int * __restrict rgb_shift) -{ - yuv422p_to_rgb(dst_buffer, in_frame, width, height, pitch, rgb_shift, true); -} - -/** - * Changes pixel format from planar YUV 420 to packed RGB/A. - * Color space is assumed ITU-T Rec. 609. YUV is expected to be full scale (aka in JPEG). - */ -static inline void yuv420p_to_rgb(char * __restrict dst_buffer, AVFrame * __restrict in_frame, +static inline void yuv8p_to_rgb(int subsampling, char * __restrict dst_buffer, AVFrame * __restrict in_frame, int width, int height, int pitch, int * __restrict rgb_shift, bool rgba) { UNUSED(rgb_shift); + UNUSED(subsampling); for(int y = 0; y < height / 2; ++y) { unsigned char *src_y1 = (unsigned char *) in_frame->data[0] + in_frame->linesize[0] * y * 2; unsigned char *src_y2 = (unsigned char *) in_frame->data[0] + in_frame->linesize[0] * (y * 2 + 1); - unsigned char *src_cb = (unsigned char *) in_frame->data[1] + in_frame->linesize[1] * y; - unsigned char *src_cr = (unsigned char *) in_frame->data[2] + in_frame->linesize[2] * y; unsigned char *dst1 = (unsigned char *) dst_buffer + pitch * (y * 2); unsigned char *dst2 = (unsigned char *) dst_buffer + pitch * (y * 2 + 1); + unsigned char *src_cb1; + unsigned char *src_cr1; + unsigned char *src_cb2; + unsigned char *src_cr2; + if (subsampling == 420) { + src_cb1 = (unsigned char *) in_frame->data[1] + in_frame->linesize[1] * y; + src_cr1 = (unsigned char *) in_frame->data[2] + in_frame->linesize[2] * y; + } else { + src_cb1 = (unsigned char *) in_frame->data[1] + in_frame->linesize[1] * (y * 2); + src_cr1 = (unsigned char *) in_frame->data[2] + in_frame->linesize[2] * (y * 2); + src_cb2 = (unsigned char *) in_frame->data[1] + in_frame->linesize[1] * (y * 2 + 1); + src_cr2 = (unsigned char *) in_frame->data[2] + in_frame->linesize[2] * (y * 2 + 1); + } + +#define WRITE_RES_YUV8P_TO_RGB(DST) if (rgba) {\ + *((uint32_t *) DST) = (MIN(MAX((r + y) >> COMP_BASE, 1), 254) << rgb_shift[R] | MIN(MAX((g + y) >> COMP_BASE, 1), 254) << rgb_shift[G] | MIN(MAX((b + y) >> COMP_BASE, 1), 254) << rgb_shift[B]);\ + DST += 4;\ + } else {\ + *DST++ = MIN(MAX((r + y) >> COMP_BASE, 1), 254);\ + *DST++ = MIN(MAX((g + y) >> COMP_BASE, 1), 254);\ + *DST++ = MIN(MAX((b + y) >> COMP_BASE, 1), 254);\ + }\ + OPTIMIZED_FOR (int x = 0; x < width / 2; ++x) { - int cb = *src_cb++ - 128; - int cr = *src_cr++ - 128; - int y = *src_y1++ << 16; - int r = 75700 * cr; - int g = -26864 * cb - 38050 * cr; - int b = 133176 * cb; - *dst1++ = MIN(MAX(r + y, 0), (1<<24) - 1) >> 16; - *dst1++ = MIN(MAX(g + y, 0), (1<<24) - 1) >> 16; - *dst1++ = MIN(MAX(b + y, 0), (1<<24) - 1) >> 16; - if (rgba) { - *dst1++ = 255; - } - y = *src_y1++ << 16; - *dst1++ = MIN(MAX(r + y, 0), (1<<24) - 1) >> 16; - *dst1++ = MIN(MAX(g + y, 0), (1<<24) - 1) >> 16; - *dst1++ = MIN(MAX(b + y, 0), (1<<24) - 1) >> 16; - if (rgba) { - *dst1++ = 255; - } - y = *src_y2++ << 16; - *dst2++ = MIN(MAX(r + y, 0), (1<<24) - 1) >> 16; - *dst2++ = MIN(MAX(g + y, 0), (1<<24) - 1) >> 16; - *dst2++ = MIN(MAX(b + y, 0), (1<<24) - 1) >> 16; - if (rgba) { - *dst2++ = 255; - } - y = *src_y2++ << 16; - *dst2++ = MIN(MAX(r + y, 0), (1<<24) - 1) >> 16; - *dst2++ = MIN(MAX(g + y, 0), (1<<24) - 1) >> 16; - *dst2++ = MIN(MAX(b + y, 0), (1<<24) - 1) >> 16; - if (rgba) { - *dst2++ = 255; + comp_type_t cb = *src_cb1++ - 128; + comp_type_t cr = *src_cr1++ - 128; + comp_type_t y = *src_y1++ * y_scale; + comp_type_t r = r_cr * cr; + comp_type_t g = g_cb * cb + g_cr * cr; + comp_type_t b = b_cb * cb; + WRITE_RES_YUV8P_TO_RGB(dst1) + + y = *src_y1++ * y_scale; + WRITE_RES_YUV8P_TO_RGB(dst1) + + if (subsampling == 422) { + cb = *src_cb2++ - 128; + cr = *src_cr2++ - 128; + r = r_cr * cr; + g = g_cb * cb + g_cr * cr; + b = b_cb * cb; } + y = *src_y2++ * y_scale; + WRITE_RES_YUV8P_TO_RGB(dst2) + + y = *src_y2++ * y_scale; + WRITE_RES_YUV8P_TO_RGB(dst2) } } } @@ -1817,15 +1779,28 @@ static inline void yuv420p_to_rgb(char * __restrict dst_buffer, AVFrame * __rest static void yuv420p_to_rgb24(char * __restrict dst_buffer, AVFrame * __restrict in_frame, int width, int height, int pitch, int * __restrict rgb_shift) { - yuv420p_to_rgb(dst_buffer, in_frame, width, height, pitch, rgb_shift, false); + yuv8p_to_rgb(420, dst_buffer, in_frame, width, height, pitch, rgb_shift, false); } static void yuv420p_to_rgb32(char * __restrict dst_buffer, AVFrame * __restrict in_frame, int width, int height, int pitch, int * __restrict rgb_shift) { - yuv420p_to_rgb(dst_buffer, in_frame, width, height, pitch, rgb_shift, true); + yuv8p_to_rgb(420, dst_buffer, in_frame, width, height, pitch, rgb_shift, true); } +static void yuv422p_to_rgb24(char * __restrict dst_buffer, AVFrame * __restrict in_frame, + int width, int height, int pitch, int * __restrict rgb_shift) +{ + yuv8p_to_rgb(422, dst_buffer, in_frame, width, height, pitch, rgb_shift, false); +} + +static void yuv422p_to_rgb32(char * __restrict dst_buffer, AVFrame * __restrict in_frame, + int width, int height, int pitch, int * __restrict rgb_shift) +{ + yuv8p_to_rgb(422, dst_buffer, in_frame, width, height, pitch, rgb_shift, true); +} + + /** * Changes pixel format from planar YUV 444 to packed RGB/A. * Color space is assumed ITU-T Rec. 609. YUV is expected to be full scale (aka in JPEG). @@ -1843,15 +1818,17 @@ static inline void yuv444p_to_rgb(char * __restrict dst_buffer, AVFrame * __rest OPTIMIZED_FOR (int x = 0; x < width; ++x) { int cb = *src_cb++ - 128; int cr = *src_cr++ - 128; - int y = *src_y++ << 16; - int r = 75700 * cr; - int g = -26864 * cb - 38050 * cr; - int b = 133176 * cb; - *dst++ = MIN(MAX(r + y, 0), (1<<24) - 1) >> 16; - *dst++ = MIN(MAX(g + y, 0), (1<<24) - 1) >> 16; - *dst++ = MIN(MAX(b + y, 0), (1<<24) - 1) >> 16; + int y = *src_y++ << COMP_BASE; + int r = r_cr * cr; + int g = g_cb * cb + g_cr * cr; + int b = b_cb * cb; if (rgba) { - *dst++ = 255; + *((uint32_t *) dst) = (MIN(MAX((r + y) >> COMP_BASE, 1), 254) << rgb_shift[R] | MIN(MAX((g + y) >> COMP_BASE, 1), 254) << rgb_shift[G] | MIN(MAX((b + y) >> COMP_BASE, 1), 254) << rgb_shift[B]); + dst += 4; + } else { + *dst++ = MIN(MAX((r + y) >> COMP_BASE, 1), 254); + *dst++ = MIN(MAX((g + y) >> COMP_BASE, 1), 254); + *dst++ = MIN(MAX((b + y) >> COMP_BASE, 1), 254); } } } @@ -2441,6 +2418,7 @@ const struct av_to_uv_conversion *get_av_to_uv_conversions() { {AV_PIX_FMT_YUV444P, RGB, yuv444p_to_rgb24, false}, {AV_PIX_FMT_YUV444P, RGBA, yuv444p_to_rgb32, false}, // 8-bit YUV (JPEG color range) + /// @todo really convert to Rec 601 full range {AV_PIX_FMT_YUVJ420P, v210, yuv420p_to_v210, false}, {AV_PIX_FMT_YUVJ420P, UYVY, yuv420p_to_uyvy, true}, {AV_PIX_FMT_YUVJ420P, RGB, yuv420p_to_rgb24, false}, From a51574288104529ded80199266801da816efa845 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Tue, 22 Sep 2020 16:05:37 +0200 Subject: [PATCH 59/74] Libavcodec common: rewritten 10-bit YUV to RGB convs --- src/libavcodec_common.c | 126 ++++++++++++++++++++++++---------------- 1 file changed, 75 insertions(+), 51 deletions(-) diff --git a/src/libavcodec_common.c b/src/libavcodec_common.c index 3383cbc6f..4ef176469 100644 --- a/src/libavcodec_common.c +++ b/src/libavcodec_common.c @@ -2104,63 +2104,87 @@ static void yuv444p10le_to_uyvy(char * __restrict dst_buffer, AVFrame * __restri } } -static inline void yuv420p10le_to_rgb(char * __restrict dst_buffer, AVFrame * __restrict in_frame, - int width, int height, int pitch, int * __restrict rgb_shift, bool rgba) +static inline void yuvp10le_to_rgb(int subsampling, char * __restrict dst_buffer, AVFrame * __restrict frame, + int width, int height, int pitch, int * __restrict rgb_shift, int out_bit_depth) { - decoder_t decoder = rgba ? vc_copylineUYVYtoRGBA : vc_copylineUYVYtoRGB; - int linesize = vc_get_linesize(width, rgba ? RGBA : RGB); - char *tmp = malloc(vc_get_linesize(width, UYVY) * height); - char *uyvy = tmp; - yuv420p10le_to_uyvy(uyvy, in_frame, width, height, vc_get_linesize(width, UYVY), rgb_shift); - for (int i = 0; i < height; i++) { - decoder((unsigned char *) dst_buffer, (unsigned char *) uyvy, linesize, - rgb_shift[R], rgb_shift[G], rgb_shift[B]); - uyvy += vc_get_linesize(width, UYVY); - dst_buffer += pitch; + assert(subsampling == 422 || subsampling == 420); + for (int y = 0; y < height / 2; ++y) { + uint16_t * __restrict src_y1 = (uint16_t *) (frame->data[0] + frame->linesize[0] * 2 * y); + uint16_t * __restrict src_y2 = (uint16_t *) (frame->data[0] + frame->linesize[0] * (2 * y + 1)); + uint16_t * __restrict src_cb1; + uint16_t * __restrict src_cr1; + uint16_t * __restrict src_cb2; + uint16_t * __restrict src_cr2; + if (subsampling == 420) { + src_cb1 = src_cb2 = (uint16_t *) (frame->data[1] + frame->linesize[1] * y); + src_cr1 = src_cr2 = (uint16_t *) (frame->data[2] + frame->linesize[2] * y); + } else { + src_cb1 = (uint16_t *) (frame->data[1] + frame->linesize[1] * (2 * y)); + src_cb2 = (uint16_t *) (frame->data[1] + frame->linesize[1] * (2 * y + 1)); + src_cr1 = (uint16_t *) (frame->data[2] + frame->linesize[2] * (2 * y)); + src_cr2 = (uint16_t *) (frame->data[2] + frame->linesize[2] * (2 * y + 1)); + } + unsigned char *dst1 = (unsigned char *) dst_buffer + (2 * y) * pitch; + unsigned char *dst2 = (unsigned char *) dst_buffer + (2 * y + 1) * pitch; + + OPTIMIZED_FOR (int x = 0; x < width / 2; ++x) { + comp_type_t cr = *src_cr1++ - (1<<9); + comp_type_t cb = *src_cb1++ - (1<<9); + comp_type_t rr = YCBCR_TO_R_709_SCALED(0, cb, cr) >> (COMP_BASE + 2); + comp_type_t gg = YCBCR_TO_G_709_SCALED(0, cb, cr) >> (COMP_BASE + 2); + comp_type_t bb = YCBCR_TO_B_709_SCALED(0, cb, cr) >> (COMP_BASE + 2); + +# define WRITE_RES_YUV10P_TO_RGB(Y, DST) {\ + comp_type_t r = Y + rr;\ + comp_type_t g = Y + gg;\ + comp_type_t b = Y + bb;\ + r = MIN((255<<0) - 1, MAX(1<<0, r));\ + g = MIN((255<<0) - 1, MAX(1<<0, g));\ + b = MIN((255<<0) - 1, MAX(1<<0, b));\ + if (out_bit_depth == 32) {\ + *((uint32_t *) DST) = (r << rgb_shift[R] | g << rgb_shift[G] | b << rgb_shift[B]);\ + DST += 4;\ + } else {\ + *DST++ = r;\ + *DST++ = g;\ + *DST++ = b;\ + }\ + } + + comp_type_t y1 = (y_scale * (*src_y1++ - (1<<6))) >> (COMP_BASE + 2); + WRITE_RES_YUV10P_TO_RGB(y1, dst1) + + comp_type_t y11 = (y_scale * (*src_y1++ - (1<<6))) >> (COMP_BASE + 2); + WRITE_RES_YUV10P_TO_RGB(y11, dst1) + + if (subsampling == 422) { + cr = *src_cr2++ - (1<<9); + cb = *src_cb2++ - (1<<9); + rr = YCBCR_TO_R_709_SCALED(0, cb, cr) >> (COMP_BASE + 2); + gg = YCBCR_TO_G_709_SCALED(0, cb, cr) >> (COMP_BASE + 2); + bb = YCBCR_TO_B_709_SCALED(0, cb, cr) >> (COMP_BASE + 2); + } + + comp_type_t y2 = (y_scale * (*src_y2++ - (1<<6))) >> (COMP_BASE + 2); + WRITE_RES_YUV10P_TO_RGB(y2, dst2) + + comp_type_t y22 = (y_scale * (*src_y2++ - (1<<6))) >> (COMP_BASE + 2); + WRITE_RES_YUV10P_TO_RGB(y22, dst2) + } } - free(tmp); } -static inline void yuv420p10le_to_rgb24(char * __restrict dst_buffer, AVFrame * __restrict in_frame, - int width, int height, int pitch, int * __restrict rgb_shift) -{ - yuv420p10le_to_rgb(dst_buffer, in_frame, width, height, pitch, rgb_shift, false); +#define MAKE_YUV_TO_RGB_FUNCTION_NAME(subs, out_bit_depth) yuv ## subs ## p10le_to_rgb ## out_bit_depth + +#define MAKE_YUV_TO_RGB_FUNCTION(subs, out_bit_depth) static void MAKE_YUV_TO_RGB_FUNCTION_NAME(subs, out_bit_depth)(char * __restrict dst_buffer, AVFrame * __restrict in_frame,\ + int width, int height, int pitch, int * __restrict rgb_shift) {\ + yuvp10le_to_rgb(subs, dst_buffer, in_frame, width, height, pitch, rgb_shift, out_bit_depth);\ } -static inline void yuv420p10le_to_rgb32(char * __restrict dst_buffer, AVFrame * __restrict in_frame, - int width, int height, int pitch, int * __restrict rgb_shift) -{ - yuv420p10le_to_rgb(dst_buffer, in_frame, width, height, pitch, rgb_shift, true); -} - -static void yuv422p10le_to_rgb(char * __restrict dst_buffer, AVFrame * __restrict in_frame, - int width, int height, int pitch, int * __restrict rgb_shift, bool rgba) -{ - decoder_t decoder = rgba ? vc_copylineUYVYtoRGBA : vc_copylineUYVYtoRGB; - int linesize = vc_get_linesize(width, rgba ? RGBA : RGB); - char *tmp = malloc(vc_get_linesize(width, UYVY) * height); - char *uyvy = tmp; - yuv422p10le_to_uyvy(uyvy, in_frame, width, height, vc_get_linesize(width, UYVY), rgb_shift); - for (int i = 0; i < height; i++) { - decoder((unsigned char *) dst_buffer, (unsigned char *) uyvy, linesize, - rgb_shift[R], rgb_shift[G], rgb_shift[B]); - uyvy += vc_get_linesize(width, UYVY); - dst_buffer += pitch; - } - free(tmp); -} - -static inline void yuv422p10le_to_rgb24(char * __restrict dst_buffer, AVFrame * __restrict in_frame, - int width, int height, int pitch, int * __restrict rgb_shift) -{ - yuv422p10le_to_rgb(dst_buffer, in_frame, width, height, pitch, rgb_shift, false); -} - -static inline void yuv422p10le_to_rgb32(char * __restrict dst_buffer, AVFrame * __restrict in_frame, - int width, int height, int pitch, int * __restrict rgb_shift) -{ - yuv422p10le_to_rgb(dst_buffer, in_frame, width, height, pitch, rgb_shift, true); -} +MAKE_YUV_TO_RGB_FUNCTION(420, 24) +MAKE_YUV_TO_RGB_FUNCTION(420, 32) +MAKE_YUV_TO_RGB_FUNCTION(422, 24) +MAKE_YUV_TO_RGB_FUNCTION(422, 32) static inline void yuv444p10le_to_rgb(char * __restrict dst_buffer, AVFrame * __restrict in_frame, int width, int height, int pitch, int * __restrict rgb_shift, bool rgba) From e1ac0c8868b1227996a93ec8f8e1c407d81985be Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Fri, 25 Sep 2020 09:17:36 +0200 Subject: [PATCH 60/74] Libavcodec common: updates --- src/libavcodec_common.c | 47 +++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/src/libavcodec_common.c b/src/libavcodec_common.c index 4ef176469..472eebec7 100644 --- a/src/libavcodec_common.c +++ b/src/libavcodec_common.c @@ -195,15 +195,20 @@ bool libav_codec_has_extradata(codec_t codec) { // // /* @brief Color space coedfficients - RGB full range to YCbCr bt. 709 limited range + * + * RGB should use SDI full range [1<<(depth-8)..255<<(depth-8)-1], see [limits] * * Scaled by 1<<COMP_BASE, footroom 16/255, headroom 235/255 (luma), 240/255 (chroma); limits [2^(depth-8)..255*2^(depth-8)-1] * matrix Y = [ 0.182586, 0.614231, 0.062007; -0.100643, -0.338572, 0.4392157; 0.4392157, -0.398942, -0.040274 ] - * * [coefficients](https://gist.github.com/yohhoy/dafa5a47dade85d8b40625261af3776a) - * * [SDI limits](https://tech.ebu.ch/docs/r/r103.pdf) + * * [coefficients]: https://gist.github.com/yohhoy/dafa5a47dade85d8b40625261af3776a "Rec. 709 coefficients" + * * [limits]: https://tech.ebu.ch/docs/r/r103.pdf "SDI limits" * @todo * Use this transformations in all conversions. * @{ */ +#define FULL_FOOT(depth) (1<<((depth)-8)) +#define FULL_HEAD(depth) ((255<<((depth)-8))-1) +#define CLAMP_FULL(val, depth) MIN(FULL_HEAD(depth), MAX((val), FULL_FOOT(depth))) typedef int32_t comp_type_t; // int32_t provides much better performance than int_fast32_t #define COMP_BASE (sizeof(comp_type_t) == 4 ? 15 : 18) // computation will be less precise when comp_type_t is 32 bit static_assert(sizeof(comp_type_t) * 8 >= COMP_BASE + 17, "comp_type_t not wide enough (we are computing in up to 16 bits!)"); @@ -236,6 +241,8 @@ static const comp_type_t b_cb = 2.112402 * (1<<COMP_BASE); #define YCBCR_TO_B_709_SCALED(y, cb, cr) ((y) /* * b_y */ + (cb) * b_cb /* + (cr) * b_cr */) /// @} +#define FORMAT_RGBA(r, g, b, depth) (CLAMP_FULL((r), (depth)) << rgb_shift[R] | CLAMP_FULL((g), (depth)) << rgb_shift[G] | CLAMP_FULL((b), (depth)) << rgb_shift[B]) + static void uyvy_to_yuv420p(AVFrame * __restrict out_frame, unsigned char * __restrict in_data, int width, int height) { int y; @@ -1120,9 +1127,9 @@ static void yuv444pXXle_to_r10k(int depth, char * __restrict dst_buffer, AVFrame comp_type_t b = YCBCR_TO_B_709_SCALED(y, cb, cr) >> (COMP_BASE-10+depth); // r g b is now on 10 bit scale - r = MIN((255<<2) - 1, MAX(1<<2, r)); - g = MIN((255<<2) - 1, MAX(1<<2, g)); - b = MIN((255<<2) - 1, MAX(1<<2, b)); + r = CLAMP_FULL(r, 10); + g = CLAMP_FULL(g, 10); + b = CLAMP_FULL(b, 10); *dst++ = r >> 2; *dst++ = (r & 0x3) << 6 | g >> 4; @@ -1171,9 +1178,9 @@ static void yuv444pXXle_to_r12l(int depth, char * __restrict dst_buffer, AVFrame comp_type_t rr = YCBCR_TO_R_709_SCALED(y, cb, cr) >> (COMP_BASE-12+depth); comp_type_t gg = YCBCR_TO_G_709_SCALED(y, cb, cr) >> (COMP_BASE-12+depth); comp_type_t bb = YCBCR_TO_B_709_SCALED(y, cb, cr) >> (COMP_BASE-12+depth); - r[j] = MIN((255<<4) - 1, MAX(1<<4, rr)); - g[j] = MIN((255<<4) - 1, MAX(1<<4, gg)); - b[j] = MIN((255<<4) - 1, MAX(1<<4, bb)); + r[j] = CLAMP_FULL(rr, 12); + g[j] = CLAMP_FULL(gg, 12); + b[j] = CLAMP_FULL(bb, 12); } dst[BYTE_SWAP(0)] = r[0] & 0xff; @@ -1740,12 +1747,12 @@ static inline void yuv8p_to_rgb(int subsampling, char * __restrict dst_buffer, A } #define WRITE_RES_YUV8P_TO_RGB(DST) if (rgba) {\ - *((uint32_t *) DST) = (MIN(MAX((r + y) >> COMP_BASE, 1), 254) << rgb_shift[R] | MIN(MAX((g + y) >> COMP_BASE, 1), 254) << rgb_shift[G] | MIN(MAX((b + y) >> COMP_BASE, 1), 254) << rgb_shift[B]);\ + *((uint32_t *) DST) = FORMAT_RGBA((r + y) >> COMP_BASE, (g + y) >> COMP_BASE, (b + y) >> COMP_BASE, 8);\ DST += 4;\ } else {\ - *DST++ = MIN(MAX((r + y) >> COMP_BASE, 1), 254);\ - *DST++ = MIN(MAX((g + y) >> COMP_BASE, 1), 254);\ - *DST++ = MIN(MAX((b + y) >> COMP_BASE, 1), 254);\ + *DST++ = CLAMP_FULL((r + y) >> COMP_BASE, 8);\ + *DST++ = CLAMP_FULL((g + y) >> COMP_BASE, 8);\ + *DST++ = CLAMP_FULL((b + y) >> COMP_BASE, 8);\ }\ OPTIMIZED_FOR (int x = 0; x < width / 2; ++x) { @@ -2138,9 +2145,9 @@ static inline void yuvp10le_to_rgb(int subsampling, char * __restrict dst_buffer comp_type_t r = Y + rr;\ comp_type_t g = Y + gg;\ comp_type_t b = Y + bb;\ - r = MIN((255<<0) - 1, MAX(1<<0, r));\ - g = MIN((255<<0) - 1, MAX(1<<0, g));\ - b = MIN((255<<0) - 1, MAX(1<<0, b));\ + r = CLAMP_FULL(r, 8);\ + g = CLAMP_FULL(g, 8);\ + b = CLAMP_FULL(b, 8);\ if (out_bit_depth == 32) {\ *((uint32_t *) DST) = (r << rgb_shift[R] | g << rgb_shift[G] | b << rgb_shift[B]);\ DST += 4;\ @@ -2203,12 +2210,12 @@ static inline void yuv444p10le_to_rgb(char * __restrict dst_buffer, AVFrame * __ comp_type_t g = g_cb * cb + g_cr * cr; comp_type_t b = b_cb * cb; if (rgba) { - *(uint32_t *)(void *) dst = (MIN(MAX(r + y, 0), (1<<24) - 1) >> COMP_BASE) << rgb_shift[0] | (MIN(MAX(g + y, 0), (1<<24) - 1) >> COMP_BASE) << rgb_shift[1] | - (MIN(MAX(b + y, 0), (1<<24) - 1) >> COMP_BASE) << rgb_shift[2]; + *(uint32_t *)(void *) dst = FORMAT_RGBA((r + y) >> COMP_BASE, (g + y) >> COMP_BASE, (b + y) >> COMP_BASE, 8); + dst += 4; } else { - *dst++ = MIN(MAX((r + y) >> (COMP_BASE), 0), (1<<8) - 1); - *dst++ = MIN(MAX((g + y) >> (COMP_BASE), 0), (1<<8) - 1); - *dst++ = MIN(MAX((b + y) >> (COMP_BASE), 0), (1<<8) - 1); + *dst++ = CLAMP_FULL((r + y) >> COMP_BASE, 8); + *dst++ = CLAMP_FULL((g + y) >> COMP_BASE, 8); + *dst++ = CLAMP_FULL((b + y) >> COMP_BASE, 8); } } } From 4a33cf560f6fe7a12d8fbdf3867283cbe5d72db2 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Fri, 25 Sep 2020 11:03:03 +0200 Subject: [PATCH 61/74] Lavc common: rewritten also nv12_to_rgb --- src/libavcodec_common.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/libavcodec_common.c b/src/libavcodec_common.c index 472eebec7..07cdd8b74 100644 --- a/src/libavcodec_common.c +++ b/src/libavcodec_common.c @@ -1682,24 +1682,29 @@ static inline void nv12_to_rgb(char * __restrict dst_buffer, AVFrame * __restric unsigned char *dst = (unsigned char *) dst_buffer + pitch * y; OPTIMIZED_FOR (int x = 0; x < width / 2; ++x) { - int cb = *src_cbcr++ - 128; - int cr = *src_cbcr++ - 128; - int y = *src_y++ << 16; - int r = 75700 * cr; - int g = -26864 * cb - 38050 * cr; - int b = 133176 * cb; - *dst++ = MIN(MAX(r + y, 0), (1<<24) - 1) >> 16; - *dst++ = MIN(MAX(g + y, 0), (1<<24) - 1) >> 16; - *dst++ = MIN(MAX(b + y, 0), (1<<24) - 1) >> 16; + comp_type_t cb = *src_cbcr++ - 128; + comp_type_t cr = *src_cbcr++ - 128; + comp_type_t y = *src_y++ * y_scale; + comp_type_t r = r_cr * cr; + comp_type_t g = g_cb * cb + g_cr * cr; + comp_type_t b = b_cb * cb; if (rgba) { - *dst++ = 255; + *((uint32_t *) dst) = FORMAT_RGBA((r + y) >> COMP_BASE, (g + y) >> COMP_BASE, (b + y) >> COMP_BASE, 8); + dst += 4; + } else { + *dst++ = CLAMP_FULL((r + y) >> COMP_BASE, 8); + *dst++ = CLAMP_FULL((g + y) >> COMP_BASE, 8); + *dst++ = CLAMP_FULL((b + y) >> COMP_BASE, 8); } - y = *src_y++ << 16; - *dst++ = MIN(MAX(r + y, 0), (1<<24) - 1) >> 16; - *dst++ = MIN(MAX(g + y, 0), (1<<24) - 1) >> 16; - *dst++ = MIN(MAX(b + y, 0), (1<<24) - 1) >> 16; + + y = *src_y++ * y_scale; if (rgba) { - *dst++ = 255; + *((uint32_t *) dst) = FORMAT_RGBA((r + y) >> COMP_BASE, (g + y) >> COMP_BASE, (b + y) >> COMP_BASE, 8); + dst += 4; + } else { + *dst++ = CLAMP_FULL((r + y) >> COMP_BASE, 8); + *dst++ = CLAMP_FULL((g + y) >> COMP_BASE, 8); + *dst++ = CLAMP_FULL((b + y) >> COMP_BASE, 8); } } } From ba2779e54bb02bc7bd2987ae427cf9d5499f893f Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Tue, 29 Sep 2020 07:55:31 +0200 Subject: [PATCH 62/74] Proxy: fixed help --- src/video_display/proxy.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_display/proxy.cpp b/src/video_display/proxy.cpp index 5432939cc..5852a78ba 100644 --- a/src/video_display/proxy.cpp +++ b/src/video_display/proxy.cpp @@ -131,8 +131,8 @@ static void *display_proxy_init(struct module *parent, const char *fmt, unsigned int ret; if (fmt == nullptr || strlen(fmt) == 0 || "help"s == fmt) { - cout << "Proxy is an helper display to combine (blend) multiple incoming streams.\n" - "Please do not use, intended for internal purposes!\n"; + cout << "Proxy is a helper display to combine (blend) multiple incoming streams.\n" + "Please do not use directly, intended for internal purposes!\n"; return nullptr; } From 818a1023b4ba7725f91aeb3b6f8dc2785c65ed87 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Fri, 25 Sep 2020 15:27:04 +0200 Subject: [PATCH 63/74] Lavc: set range/colorspec to codec --- src/libavcodec_common.c | 65 ++++++++++++++++++------------- src/libavcodec_common.h | 8 +++- src/video_compress/libavcodec.cpp | 19 ++++++++- 3 files changed, 63 insertions(+), 29 deletions(-) diff --git a/src/libavcodec_common.c b/src/libavcodec_common.c index 07cdd8b74..55cb4f93a 100644 --- a/src/libavcodec_common.c +++ b/src/libavcodec_common.c @@ -2369,36 +2369,36 @@ const struct uv_to_av_conversion *get_uv_to_av_conversions() { * conversions below the others. */ static const struct uv_to_av_conversion uv_to_av_conversions[] = { - { v210, AV_PIX_FMT_YUV420P10LE, v210_to_yuv420p10le }, - { v210, AV_PIX_FMT_YUV422P10LE, v210_to_yuv422p10le }, - { v210, AV_PIX_FMT_YUV444P10LE, v210_to_yuv444p10le }, - { v210, AV_PIX_FMT_YUV444P16LE, v210_to_yuv444p16le }, - { R10k, AV_PIX_FMT_YUV444P10LE, r10k_to_yuv444p10le }, - { R10k, AV_PIX_FMT_YUV444P12LE, r10k_to_yuv444p12le }, - { R10k, AV_PIX_FMT_YUV444P16LE, r10k_to_yuv444p16le }, - { R12L, AV_PIX_FMT_YUV444P16LE, r12l_to_yuv444p10le }, - { R12L, AV_PIX_FMT_YUV444P16LE, r12l_to_yuv444p12le }, - { R12L, AV_PIX_FMT_YUV444P16LE, r12l_to_yuv444p16le }, + { v210, AV_PIX_FMT_YUV420P10LE, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, v210_to_yuv420p10le }, + { v210, AV_PIX_FMT_YUV422P10LE, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, v210_to_yuv422p10le }, + { v210, AV_PIX_FMT_YUV444P10LE, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, v210_to_yuv444p10le }, + { v210, AV_PIX_FMT_YUV444P16LE, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, v210_to_yuv444p16le }, + { R10k, AV_PIX_FMT_YUV444P10LE, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, r10k_to_yuv444p10le }, + { R10k, AV_PIX_FMT_YUV444P12LE, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, r10k_to_yuv444p12le }, + { R10k, AV_PIX_FMT_YUV444P16LE, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, r10k_to_yuv444p16le }, + { R12L, AV_PIX_FMT_YUV444P16LE, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, r12l_to_yuv444p10le }, + { R12L, AV_PIX_FMT_YUV444P16LE, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, r12l_to_yuv444p12le }, + { R12L, AV_PIX_FMT_YUV444P16LE, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, r12l_to_yuv444p16le }, #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(55, 15, 100) // FFMPEG commit c2869b4640f - { v210, AV_PIX_FMT_P010LE, v210_to_p010le }, + { v210, AV_PIX_FMT_P010LE, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, v210_to_p010le }, #endif - { UYVY, AV_PIX_FMT_YUV422P, uyvy_to_yuv422p }, - { UYVY, AV_PIX_FMT_YUVJ422P, uyvy_to_yuv422p }, - { UYVY, AV_PIX_FMT_YUV420P, uyvy_to_yuv420p }, - { UYVY, AV_PIX_FMT_YUVJ420P, uyvy_to_yuv420p }, - { UYVY, AV_PIX_FMT_NV12, uyvy_to_nv12 }, - { UYVY, AV_PIX_FMT_YUV444P, uyvy_to_yuv444p }, - { UYVY, AV_PIX_FMT_YUVJ444P, uyvy_to_yuv444p }, - { RGB, AV_PIX_FMT_BGR0, rgb_to_bgr0 }, - { RGB, AV_PIX_FMT_GBRP, rgb_to_gbrp }, - { RGBA, AV_PIX_FMT_GBRP, rgba_to_gbrp }, - { R10k, AV_PIX_FMT_BGR0, r10k_to_bgr0 }, - { R10k, AV_PIX_FMT_GBRP10LE, r10k_to_gbrp10le }, - { R10k, AV_PIX_FMT_YUV422P10LE, r10k_to_yuv422p10le }, + { UYVY, AV_PIX_FMT_YUV422P, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, uyvy_to_yuv422p }, + { UYVY, AV_PIX_FMT_YUVJ422P, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, uyvy_to_yuv422p }, + { UYVY, AV_PIX_FMT_YUV420P, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, uyvy_to_yuv420p }, + { UYVY, AV_PIX_FMT_YUVJ420P, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, uyvy_to_yuv420p }, + { UYVY, AV_PIX_FMT_NV12, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, uyvy_to_nv12 }, + { UYVY, AV_PIX_FMT_YUV444P, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, uyvy_to_yuv444p }, + { UYVY, AV_PIX_FMT_YUVJ444P, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, uyvy_to_yuv444p }, + { RGB, AV_PIX_FMT_BGR0, AVCOL_SPC_RGB, AVCOL_RANGE_JPEG, rgb_to_bgr0 }, + { RGB, AV_PIX_FMT_GBRP, AVCOL_SPC_RGB, AVCOL_RANGE_JPEG, rgb_to_gbrp }, + { RGBA, AV_PIX_FMT_GBRP, AVCOL_SPC_RGB, AVCOL_RANGE_JPEG, rgba_to_gbrp }, + { R10k, AV_PIX_FMT_BGR0, AVCOL_SPC_RGB, AVCOL_RANGE_JPEG, r10k_to_bgr0 }, + { R10k, AV_PIX_FMT_GBRP10LE, AVCOL_SPC_RGB, AVCOL_RANGE_JPEG, r10k_to_gbrp10le }, + { R10k, AV_PIX_FMT_YUV422P10LE, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, r10k_to_yuv422p10le }, #ifdef HAVE_12_AND_14_PLANAR_COLORSPACES - { R12L, AV_PIX_FMT_GBRP12LE, r12l_to_gbrp12le }, + { R12L, AV_PIX_FMT_GBRP12LE, AVCOL_SPC_RGB, AVCOL_RANGE_JPEG, r12l_to_gbrp12le }, #endif - { 0, 0, 0 } + { 0, 0, 0, 0, 0 } }; return uv_to_av_conversions; } @@ -2415,6 +2415,19 @@ pixfmt_callback_t get_uv_to_av_conversion(codec_t uv_codec, int av_codec) { return NULL; } +void get_av_pixfmt_details(codec_t uv_codec, int av_codec, enum AVColorSpace *colorspace, enum AVColorRange *color_range) +{ + for (const struct uv_to_av_conversion *conversions = get_uv_to_av_conversions(); + conversions->func != 0; conversions++) { + if (conversions->dst == av_codec && + conversions->src == uv_codec) { + *colorspace = conversions->colorspace; + *color_range = conversions->color_range; + return; + } + } +} + /** * @brief returns list of available conversion. Terminated by uv_to_av_conversion::uv_codec == VIDEO_CODEC_NONE */ diff --git a/src/libavcodec_common.h b/src/libavcodec_common.h index 6198d57f5..b6ec11f4f 100644 --- a/src/libavcodec_common.h +++ b/src/libavcodec_common.h @@ -189,10 +189,16 @@ typedef uv_to_av_convert *pixfmt_callback_t; struct uv_to_av_conversion { codec_t src; enum AVPixelFormat dst; - pixfmt_callback_t func; + enum AVColorSpace colorspace; ///< destination colorspace + enum AVColorRange color_range; ///< destination color range + pixfmt_callback_t func; ///< conversion function }; const struct uv_to_av_conversion *get_uv_to_av_conversions(void); pixfmt_callback_t get_uv_to_av_conversion(codec_t uv_codec, int av_codec); +/** + * Returns AV format details for given pair UV,AV codec (must be unique then) + */ +void get_av_pixfmt_details(codec_t uv_codec, int av_codec, enum AVColorSpace *colorspace, enum AVColorRange *color_range); typedef void av_to_uv_convert(char * __restrict dst_buffer, AVFrame * __restrict in_frame, int width, int height, int pitch, int * __restrict rgb_shift); typedef av_to_uv_convert *av_to_uv_convert_p; diff --git a/src/video_compress/libavcodec.cpp b/src/video_compress/libavcodec.cpp index 446fb9a31..0738c6297 100644 --- a/src/video_compress/libavcodec.cpp +++ b/src/video_compress/libavcodec.cpp @@ -937,7 +937,7 @@ static bool try_open_codec(struct state_video_compress_libav *s, AVPixelFormat &pix_fmt, struct video_desc desc, codec_t ug_codec, - AVCodec *codec) + const AVCodec *codec) { // avcodec_alloc_context3 allocates context and sets default value s->codec_ctx = avcodec_alloc_context3(codec); @@ -967,6 +967,13 @@ static bool try_open_codec(struct state_video_compress_libav *s, pix_fmt = AV_PIX_FMT_NV12; } #endif + + if (const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get(pix_fmt)) { // defaults + s->codec_ctx->colorspace = (desc->flags & AV_PIX_FMT_FLAG_RGB) != 0U ? AVCOL_SPC_RGB : AVCOL_SPC_BT709; + s->codec_ctx->color_range = (desc->flags & AV_PIX_FMT_FLAG_RGB) != 0U ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; + } + get_av_pixfmt_details(ug_codec, pix_fmt, &s->codec_ctx->colorspace, &s->codec_ctx->color_range); + /* open it */ pthread_mutex_lock(s->lavcd_global_lock); if (avcodec_open2(s->codec_ctx, codec, NULL) < 0) { @@ -976,8 +983,16 @@ static bool try_open_codec(struct state_video_compress_libav *s, pthread_mutex_unlock(s->lavcd_global_lock); return false; } - pthread_mutex_unlock(s->lavcd_global_lock); + + // setting again for JPEG encoder because avcodec_open2 overrides color_range from MPEG to JPEG for YUVJ* pix_fmts + if (pix_fmt == AV_PIX_FMT_YUVJ420P || pix_fmt == AV_PIX_FMT_YUVJ411P || pix_fmt == AV_PIX_FMT_YUVJ422P || pix_fmt == AV_PIX_FMT_YUVJ440P || pix_fmt == AV_PIX_FMT_YUVJ444P) { + if (const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get(pix_fmt)) { // defaults + s->codec_ctx->colorspace = (desc->flags & AV_PIX_FMT_FLAG_RGB) != 0U ? AVCOL_SPC_RGB : AVCOL_SPC_BT709; + s->codec_ctx->color_range = (desc->flags & AV_PIX_FMT_FLAG_RGB) != 0U ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; + } + get_av_pixfmt_details(ug_codec, pix_fmt, &s->codec_ctx->colorspace, &s->codec_ctx->color_range); + } return true; } From 6b4f2ddfce8b0051f1aa611223ff507c5cc83ca7 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Tue, 29 Sep 2020 14:19:41 +0200 Subject: [PATCH 64/74] Conversions from JPEG full-range 601 Only full->limited range YCbCr is performed --- src/libavcodec_common.c | 100 ++++++++++++++++++++++++++---- src/video_decompress/libavcodec.c | 1 + 2 files changed, 88 insertions(+), 13 deletions(-) diff --git a/src/libavcodec_common.c b/src/libavcodec_common.c index 55cb4f93a..42c89ddc0 100644 --- a/src/libavcodec_common.c +++ b/src/libavcodec_common.c @@ -1484,6 +1484,89 @@ static void yuv420p_to_uyvy(char * __restrict dst_buffer, AVFrame * __restrict i } } +/// converts YUV full range to limited range +static inline void yuvj42Xp_to_uyvy(int subsampling, char * __restrict dst_buffer, AVFrame * __restrict in_frame, + int width, int height, int pitch, int * __restrict rgb_shift) +{ + UNUSED(rgb_shift); + for(int y = 0; y < (height + 1) / 2; ++y) { + int scnd_row = y * 2 + 1; + if (scnd_row == height) { + scnd_row = height - 1; + } + unsigned char *src_y1 = (unsigned char *) in_frame->data[0] + in_frame->linesize[0] * y * 2; + unsigned char *src_y2 = (unsigned char *) in_frame->data[0] + in_frame->linesize[0] * scnd_row; + unsigned char *dst1 = (unsigned char *) dst_buffer + (y * 2) * pitch; + unsigned char *dst2 = (unsigned char *) dst_buffer + scnd_row * pitch; + unsigned char *src_cb1; + unsigned char *src_cr1; + unsigned char *src_cb2; + unsigned char *src_cr2; + if (subsampling == 420) { + src_cb1 = (unsigned char *) in_frame->data[1] + in_frame->linesize[1] * y; + src_cr1 = (unsigned char *) in_frame->data[2] + in_frame->linesize[2] * y; + } else { + src_cb1 = (unsigned char *) in_frame->data[1] + in_frame->linesize[1] * (y * 2); + src_cr1 = (unsigned char *) in_frame->data[2] + in_frame->linesize[2] * (y * 2); + src_cb2 = (unsigned char *) in_frame->data[1] + in_frame->linesize[1] * (y * 2 + 1); + src_cr2 = (unsigned char *) in_frame->data[2] + in_frame->linesize[2] * (y * 2 + 1); + } + + OPTIMIZED_FOR (int x = 0; x < width - 1; x += 2) { + int cb = ((*src_cb1++ - 128) * 224 / 255) + 128; + int cr = ((*src_cr1++ - 128) * 224 / 255) + 128; + *dst1++ = cb; + *dst1++ = 16 + (*src_y1++ * 219 / 255); + *dst1++ = cr; + *dst1++ = 16 + (*src_y1++ * 219 / 255); + + if (subsampling == 422) { + cb = ((*src_cb2++ - 128) * 224 / 255) + 128; + cr = ((*src_cr2++ - 128) * 224 / 255) + 128; + } + + *dst2++ = cb; + *dst2++ = 16 + (*src_y2++ * 219 / 255); + *dst2++ = cr; + *dst2++ = 16 + (*src_y2++ * 219 / 255); + } + } +} + +static void yuvj420p_to_uyvy(char * __restrict dst_buffer, AVFrame * __restrict in_frame, + int width, int height, int pitch, int * __restrict rgb_shift) +{ + yuvj42Xp_to_uyvy(420, dst_buffer, in_frame, width, height, pitch, rgb_shift); +} + +static void yuvj422p_to_uyvy(char * __restrict dst_buffer, AVFrame * __restrict in_frame, + int width, int height, int pitch, int * __restrict rgb_shift) +{ + yuvj42Xp_to_uyvy(422, dst_buffer, in_frame, width, height, pitch, rgb_shift); +} + +static void yuvj444p_to_uyvy(char * __restrict dst_buffer, AVFrame * __restrict in_frame, + int width, int height, int pitch, int * __restrict rgb_shift) +{ + UNUSED(rgb_shift); + for(int y = 0; y < height; ++y) { + unsigned char *src_y = (unsigned char *) in_frame->data[0] + in_frame->linesize[0] * y; + unsigned char *src_cb = (unsigned char *) in_frame->data[1] + in_frame->linesize[1] * y; + unsigned char *src_cr = (unsigned char *) in_frame->data[2] + in_frame->linesize[2] * y; + unsigned char *dst = (unsigned char *) dst_buffer + y * pitch; + OPTIMIZED_FOR (int x = 0; x < width - 1; x += 2) { + int cb = ((src_cb[0] + src_cb[1] - 256) * 112 / 255) + 128; + int cr = ((src_cr[0] + src_cr[1] - 256) * 112 / 255) + 128; + src_cb += 2; + src_cr += 2; + *dst++ = cb; + *dst++ = 16 + (*src_y++ * 219 / 255); + *dst++ = cr; + *dst++ = 16 + (*src_y++ * 219 / 255); + } + } +} + static void yuv420p_to_v210(char * __restrict dst_buffer, AVFrame * __restrict in_frame, int width, int height, int pitch, int * __restrict rgb_shift) { @@ -2467,19 +2550,10 @@ const struct av_to_uv_conversion *get_av_to_uv_conversions() { {AV_PIX_FMT_YUV444P, RGB, yuv444p_to_rgb24, false}, {AV_PIX_FMT_YUV444P, RGBA, yuv444p_to_rgb32, false}, // 8-bit YUV (JPEG color range) - /// @todo really convert to Rec 601 full range - {AV_PIX_FMT_YUVJ420P, v210, yuv420p_to_v210, false}, - {AV_PIX_FMT_YUVJ420P, UYVY, yuv420p_to_uyvy, true}, - {AV_PIX_FMT_YUVJ420P, RGB, yuv420p_to_rgb24, false}, - {AV_PIX_FMT_YUVJ420P, RGBA, yuv420p_to_rgb32, false}, - {AV_PIX_FMT_YUVJ422P, v210, yuv422p_to_v210, false}, - {AV_PIX_FMT_YUVJ422P, UYVY, yuv422p_to_uyvy, true}, - {AV_PIX_FMT_YUVJ422P, RGB, yuv422p_to_rgb24, false}, - {AV_PIX_FMT_YUVJ422P, RGBA, yuv422p_to_rgb32, false}, - {AV_PIX_FMT_YUVJ444P, v210, yuv444p_to_v210, false}, - {AV_PIX_FMT_YUVJ444P, UYVY, yuv444p_to_uyvy, true}, - {AV_PIX_FMT_YUVJ444P, RGB, yuv444p_to_rgb24, false}, - {AV_PIX_FMT_YUVJ444P, RGBA, yuv444p_to_rgb32, false}, + /// @todo for uyvy it just converts 601 full range to limited (but still 601) + {AV_PIX_FMT_YUVJ420P, UYVY, yuvj420p_to_uyvy, true}, + {AV_PIX_FMT_YUVJ422P, UYVY, yuvj422p_to_uyvy, true}, + {AV_PIX_FMT_YUVJ444P, UYVY, yuvj444p_to_uyvy, true}, // 8-bit YUV (NV12) {AV_PIX_FMT_NV12, UYVY, nv12_to_uyvy, true}, {AV_PIX_FMT_NV12, RGB, nv12_to_rgb24, false}, diff --git a/src/video_decompress/libavcodec.c b/src/video_decompress/libavcodec.c index 47985b804..3afec962c 100644 --- a/src/video_decompress/libavcodec.c +++ b/src/video_decompress/libavcodec.c @@ -763,6 +763,7 @@ static decompress_status libavcodec_decompress(void *state, unsigned char *dst, struct timeval t4; gettimeofday(&t4, NULL); log_msg(LOG_LEVEL_DEBUG, MOD_NAME "Decompressing %c frame took %f sec, pixfmt change %f s.\n", av_get_picture_type_char(s->frame->pict_type), tv_diff(t1, t0), tv_diff(t4, t3)); + log_msg(LOG_LEVEL_DEBUG, MOD_NAME "Pixel format: %s, color space: %s, range %s\n", av_get_pix_fmt_name(s->codec_ctx->pix_fmt), av_get_colorspace_name(s->codec_ctx->colorspace), av_color_range_name(s->codec_ctx->color_range)); } if (len <= 0) { From 4bf70393df0014a846879fb6a9f9e43419ccdcd3 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Thu, 1 Oct 2020 15:17:02 +0200 Subject: [PATCH 65/74] Bump to new GPUJPEG --- gpujpeg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpujpeg b/gpujpeg index 2842002b2..d277a02f5 160000 --- a/gpujpeg +++ b/gpujpeg @@ -1 +1 @@ -Subproject commit 2842002b227be104d7ff69747da3fd5985f4683e +Subproject commit d277a02f517814c12279c55186af842a5564a18c From f00ff04d6cee81d62cb57e21db34a15cdc933f36 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Thu, 1 Oct 2020 13:31:17 +0200 Subject: [PATCH 66/74] GPUJPEG enc.: set internal format to YUV 709 (TV) for YUV --- src/video_compress/gpujpeg.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/video_compress/gpujpeg.cpp b/src/video_compress/gpujpeg.cpp index f99531f42..9b130a00a 100644 --- a/src/video_compress/gpujpeg.cpp +++ b/src/video_compress/gpujpeg.cpp @@ -314,10 +314,8 @@ bool encoder_state::configure_with(struct video_desc desc) m_encoder_param.interleaved = (codec_is_a_rgb(m_enc_input_codec) && !m_parent_state->m_force_interleaved) ? 0 : 1; - if (m_parent_state->m_use_internal_codec == RGB || - (codec_is_a_rgb(m_enc_input_codec) && !m_parent_state->m_use_internal_codec)) { - m_encoder_param.color_space_internal = GPUJPEG_RGB; - } + m_encoder_param.color_space_internal = m_parent_state->m_use_internal_codec == RGB || codec_is_a_rgb(m_enc_input_codec) + ? GPUJPEG_RGB : GPUJPEG_YCBCR_BT709; gpujpeg_image_set_default_parameters(&m_param_image); From 3c4b6b68bbf1b082c2062bda468d3c1081d3d82c Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Thu, 1 Oct 2020 13:31:17 +0200 Subject: [PATCH 67/74] GPUJPEG: pass verbose parameter --- src/video_compress/gpujpeg.cpp | 3 ++- src/video_decompress/gpujpeg.c | 12 +++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/video_compress/gpujpeg.cpp b/src/video_compress/gpujpeg.cpp index 9b130a00a..e50cf32bb 100644 --- a/src/video_compress/gpujpeg.cpp +++ b/src/video_compress/gpujpeg.cpp @@ -52,6 +52,7 @@ #include "utils/video_frame_pool.h" #include "video.h" +#include <algorithm> #include <initializer_list> #include <libgpujpeg/gpujpeg_encoder.h> #include <libgpujpeg/gpujpeg_version.h> @@ -295,7 +296,7 @@ bool encoder_state::configure_with(struct video_desc desc) m_encoder_param.restart_interval = codec_is_a_rgb(m_enc_input_codec) ? 8 : 4; } - m_encoder_param.verbose = 0; + m_encoder_param.verbose = max<int>(0, log_level - LOG_LEVEL_INFO); m_encoder_param.segment_info = 1; /* LUMA */ diff --git a/src/video_decompress/gpujpeg.c b/src/video_decompress/gpujpeg.c index 4727505bc..b1c1643b3 100644 --- a/src/video_decompress/gpujpeg.c +++ b/src/video_decompress/gpujpeg.c @@ -94,6 +94,14 @@ static int configure_with(struct state_decompress_gpujpeg *s, struct video_desc if(!s->decoder) { return FALSE; } + + struct gpujpeg_parameters param; + gpujpeg_set_default_parameters(¶m); + param.verbose = MAX(0, log_level - LOG_LEVEL_INFO); + struct gpujpeg_image_parameters param_image; + gpujpeg_image_set_default_parameters(¶m_image); + gpujpeg_decoder_init(s->decoder, ¶m, ¶m_image); + switch (s->out_codec) { case I420: gpujpeg_decoder_set_output_format(s->decoder, GPUJPEG_YCBCR_JPEG, @@ -180,7 +188,9 @@ static int gpujpeg_decompress_reconfigure(void *state, struct video_desc desc, static decompress_status gpujpeg_probe_internal_codec(unsigned char *buffer, size_t len, codec_t *internal_codec) { *internal_codec = VIDEO_CODEC_NONE; struct gpujpeg_image_parameters params = { 0 }; -#if LIBGPUJPEG_API_VERSION >= 6 +#if LIBGPUJPEG_API_VERSION >= 11 + if (gpujpeg_decoder_get_image_info(buffer, len, ¶ms, NULL, MAX(0, log_level - LOG_LEVEL_INFO)) != 0) { +#elif LIBGPUJPEG_API_VERSION >= 6 if (gpujpeg_decoder_get_image_info(buffer, len, ¶ms, NULL) != 0) { #elif LIBGPUJPEG_API_VERSION >= 5 if (gpujpeg_reader_get_image_info(buffer, len, ¶ms, NULL) != 0) { From 7f6d0748561e0c648383b4a437e4a9b957190428 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Thu, 1 Oct 2020 15:16:46 +0200 Subject: [PATCH 68/74] This reverts commit 6b4f2ddfce8b0051f1aa611223ff507c5cc83ca7. The conversions are no longer needed with new GPUJPEG that supports compressing YUV BT.709 natively. --- src/libavcodec_common.c | 103 +++++------------------------- src/video_decompress/libavcodec.c | 1 - 2 files changed, 15 insertions(+), 89 deletions(-) diff --git a/src/libavcodec_common.c b/src/libavcodec_common.c index 42c89ddc0..1b43e71b9 100644 --- a/src/libavcodec_common.c +++ b/src/libavcodec_common.c @@ -1484,89 +1484,6 @@ static void yuv420p_to_uyvy(char * __restrict dst_buffer, AVFrame * __restrict i } } -/// converts YUV full range to limited range -static inline void yuvj42Xp_to_uyvy(int subsampling, char * __restrict dst_buffer, AVFrame * __restrict in_frame, - int width, int height, int pitch, int * __restrict rgb_shift) -{ - UNUSED(rgb_shift); - for(int y = 0; y < (height + 1) / 2; ++y) { - int scnd_row = y * 2 + 1; - if (scnd_row == height) { - scnd_row = height - 1; - } - unsigned char *src_y1 = (unsigned char *) in_frame->data[0] + in_frame->linesize[0] * y * 2; - unsigned char *src_y2 = (unsigned char *) in_frame->data[0] + in_frame->linesize[0] * scnd_row; - unsigned char *dst1 = (unsigned char *) dst_buffer + (y * 2) * pitch; - unsigned char *dst2 = (unsigned char *) dst_buffer + scnd_row * pitch; - unsigned char *src_cb1; - unsigned char *src_cr1; - unsigned char *src_cb2; - unsigned char *src_cr2; - if (subsampling == 420) { - src_cb1 = (unsigned char *) in_frame->data[1] + in_frame->linesize[1] * y; - src_cr1 = (unsigned char *) in_frame->data[2] + in_frame->linesize[2] * y; - } else { - src_cb1 = (unsigned char *) in_frame->data[1] + in_frame->linesize[1] * (y * 2); - src_cr1 = (unsigned char *) in_frame->data[2] + in_frame->linesize[2] * (y * 2); - src_cb2 = (unsigned char *) in_frame->data[1] + in_frame->linesize[1] * (y * 2 + 1); - src_cr2 = (unsigned char *) in_frame->data[2] + in_frame->linesize[2] * (y * 2 + 1); - } - - OPTIMIZED_FOR (int x = 0; x < width - 1; x += 2) { - int cb = ((*src_cb1++ - 128) * 224 / 255) + 128; - int cr = ((*src_cr1++ - 128) * 224 / 255) + 128; - *dst1++ = cb; - *dst1++ = 16 + (*src_y1++ * 219 / 255); - *dst1++ = cr; - *dst1++ = 16 + (*src_y1++ * 219 / 255); - - if (subsampling == 422) { - cb = ((*src_cb2++ - 128) * 224 / 255) + 128; - cr = ((*src_cr2++ - 128) * 224 / 255) + 128; - } - - *dst2++ = cb; - *dst2++ = 16 + (*src_y2++ * 219 / 255); - *dst2++ = cr; - *dst2++ = 16 + (*src_y2++ * 219 / 255); - } - } -} - -static void yuvj420p_to_uyvy(char * __restrict dst_buffer, AVFrame * __restrict in_frame, - int width, int height, int pitch, int * __restrict rgb_shift) -{ - yuvj42Xp_to_uyvy(420, dst_buffer, in_frame, width, height, pitch, rgb_shift); -} - -static void yuvj422p_to_uyvy(char * __restrict dst_buffer, AVFrame * __restrict in_frame, - int width, int height, int pitch, int * __restrict rgb_shift) -{ - yuvj42Xp_to_uyvy(422, dst_buffer, in_frame, width, height, pitch, rgb_shift); -} - -static void yuvj444p_to_uyvy(char * __restrict dst_buffer, AVFrame * __restrict in_frame, - int width, int height, int pitch, int * __restrict rgb_shift) -{ - UNUSED(rgb_shift); - for(int y = 0; y < height; ++y) { - unsigned char *src_y = (unsigned char *) in_frame->data[0] + in_frame->linesize[0] * y; - unsigned char *src_cb = (unsigned char *) in_frame->data[1] + in_frame->linesize[1] * y; - unsigned char *src_cr = (unsigned char *) in_frame->data[2] + in_frame->linesize[2] * y; - unsigned char *dst = (unsigned char *) dst_buffer + y * pitch; - OPTIMIZED_FOR (int x = 0; x < width - 1; x += 2) { - int cb = ((src_cb[0] + src_cb[1] - 256) * 112 / 255) + 128; - int cr = ((src_cr[0] + src_cr[1] - 256) * 112 / 255) + 128; - src_cb += 2; - src_cr += 2; - *dst++ = cb; - *dst++ = 16 + (*src_y++ * 219 / 255); - *dst++ = cr; - *dst++ = 16 + (*src_y++ * 219 / 255); - } - } -} - static void yuv420p_to_v210(char * __restrict dst_buffer, AVFrame * __restrict in_frame, int width, int height, int pitch, int * __restrict rgb_shift) { @@ -2549,11 +2466,21 @@ const struct av_to_uv_conversion *get_av_to_uv_conversions() { {AV_PIX_FMT_YUV444P, UYVY, yuv444p_to_uyvy, true}, {AV_PIX_FMT_YUV444P, RGB, yuv444p_to_rgb24, false}, {AV_PIX_FMT_YUV444P, RGBA, yuv444p_to_rgb32, false}, - // 8-bit YUV (JPEG color range) - /// @todo for uyvy it just converts 601 full range to limited (but still 601) - {AV_PIX_FMT_YUVJ420P, UYVY, yuvj420p_to_uyvy, true}, - {AV_PIX_FMT_YUVJ422P, UYVY, yuvj422p_to_uyvy, true}, - {AV_PIX_FMT_YUVJ444P, UYVY, yuvj444p_to_uyvy, true}, + // 8-bit YUV - this should be supposedly full range JPEG but lavd decoder doesn't honor + // GPUJPEG's SPIFF header indicating YUV BT.709 limited range. The YUVJ pixel formats + // are detected only for GPUJPEG generated JPEGs. + {AV_PIX_FMT_YUVJ420P, v210, yuv420p_to_v210, false}, + {AV_PIX_FMT_YUVJ420P, UYVY, yuv420p_to_uyvy, true}, + {AV_PIX_FMT_YUVJ420P, RGB, yuv420p_to_rgb24, false}, + {AV_PIX_FMT_YUVJ420P, RGBA, yuv420p_to_rgb32, false}, + {AV_PIX_FMT_YUVJ422P, v210, yuv422p_to_v210, false}, + {AV_PIX_FMT_YUVJ422P, UYVY, yuv422p_to_uyvy, true}, + {AV_PIX_FMT_YUVJ422P, RGB, yuv422p_to_rgb24, false}, + {AV_PIX_FMT_YUVJ422P, RGBA, yuv422p_to_rgb32, false}, + {AV_PIX_FMT_YUVJ444P, v210, yuv444p_to_v210, false}, + {AV_PIX_FMT_YUVJ444P, UYVY, yuv444p_to_uyvy, true}, + {AV_PIX_FMT_YUVJ444P, RGB, yuv444p_to_rgb24, false}, + {AV_PIX_FMT_YUVJ444P, RGBA, yuv444p_to_rgb32, false}, // 8-bit YUV (NV12) {AV_PIX_FMT_NV12, UYVY, nv12_to_uyvy, true}, {AV_PIX_FMT_NV12, RGB, nv12_to_rgb24, false}, diff --git a/src/video_decompress/libavcodec.c b/src/video_decompress/libavcodec.c index 3afec962c..47985b804 100644 --- a/src/video_decompress/libavcodec.c +++ b/src/video_decompress/libavcodec.c @@ -763,7 +763,6 @@ static decompress_status libavcodec_decompress(void *state, unsigned char *dst, struct timeval t4; gettimeofday(&t4, NULL); log_msg(LOG_LEVEL_DEBUG, MOD_NAME "Decompressing %c frame took %f sec, pixfmt change %f s.\n", av_get_picture_type_char(s->frame->pict_type), tv_diff(t1, t0), tv_diff(t4, t3)); - log_msg(LOG_LEVEL_DEBUG, MOD_NAME "Pixel format: %s, color space: %s, range %s\n", av_get_pix_fmt_name(s->codec_ctx->pix_fmt), av_get_colorspace_name(s->codec_ctx->colorspace), av_color_range_name(s->codec_ctx->color_range)); } if (len <= 0) { From 2969894a7f1a7d5683457f18272d3bcf41a73c48 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Fri, 2 Oct 2020 08:23:15 +0200 Subject: [PATCH 69/74] GPUJPEG: set BT.709 also for I420 --- src/video_compress/gpujpeg.cpp | 2 +- src/video_decompress/gpujpeg.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_compress/gpujpeg.cpp b/src/video_compress/gpujpeg.cpp index e50cf32bb..f83bd4b5a 100644 --- a/src/video_compress/gpujpeg.cpp +++ b/src/video_compress/gpujpeg.cpp @@ -324,7 +324,7 @@ bool encoder_state::configure_with(struct video_desc desc) m_param_image.height = desc.height; m_param_image.comp_count = 3; - m_param_image.color_space = codec_is_a_rgb(m_enc_input_codec) ? GPUJPEG_RGB : (desc.color_spec == I420 ? GPUJPEG_YCBCR_JPEG : GPUJPEG_YCBCR_BT709); + m_param_image.color_space = codec_is_a_rgb(m_enc_input_codec) ? GPUJPEG_RGB : GPUJPEG_YCBCR_BT709; #if LIBGPUJPEG_API_VERSION > 2 switch (m_enc_input_codec) { diff --git a/src/video_decompress/gpujpeg.c b/src/video_decompress/gpujpeg.c index b1c1643b3..789196fdf 100644 --- a/src/video_decompress/gpujpeg.c +++ b/src/video_decompress/gpujpeg.c @@ -104,7 +104,7 @@ static int configure_with(struct state_decompress_gpujpeg *s, struct video_desc switch (s->out_codec) { case I420: - gpujpeg_decoder_set_output_format(s->decoder, GPUJPEG_YCBCR_JPEG, + gpujpeg_decoder_set_output_format(s->decoder, GPUJPEG_YCBCR_BT709, GPUJPEG_420_U8_P0P1P2); break; case RGBA: From 4ea8241a4d608c31f50e6419ff1cf1796ec6df05 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Fri, 2 Oct 2020 09:30:41 +0200 Subject: [PATCH 70/74] GPUJPEG: updated opt for internal codec Now wider range of GPUJPEG supported color spaces may be passed. --- src/video_compress/gpujpeg.cpp | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/video_compress/gpujpeg.cpp b/src/video_compress/gpujpeg.cpp index f83bd4b5a..f864af11a 100644 --- a/src/video_compress/gpujpeg.cpp +++ b/src/video_compress/gpujpeg.cpp @@ -157,9 +157,7 @@ public: int m_quality; bool m_force_interleaved = false; int m_subsampling = 0; // 444, 422 or 420; 0 -> autoselect - codec_t m_use_internal_codec = VIDEO_CODEC_NONE; // RGB or UYVY, - // VIDEO_CODEC_NONE - // if no preferrence + enum gpujpeg_color_space m_use_internal_codec = GPUJPEG_NONE; // requested internal codec synchronized_queue<shared_ptr<struct video_frame>, 1> m_out_queue; ///< queue for compressed frames mutex m_occupancy_lock; @@ -260,7 +258,7 @@ bool encoder_state::configure_with(struct video_desc desc) compressed_desc.color_spec = JPEG; if (desc.color_spec == I420) { - if (m_parent_state->m_use_internal_codec == RGB || + if ((m_parent_state->m_use_internal_codec != GPUJPEG_NONE && m_parent_state->m_use_internal_codec != GPUJPEG_YCBCR_BT709) || (m_parent_state->m_subsampling != 0 && m_parent_state->m_subsampling != 420)) { log_msg(LOG_LEVEL_ERROR, MOD_NAME "Converting from planar pixel formats is " "possible only without subsampling/color space change.\n"); @@ -314,9 +312,12 @@ bool encoder_state::configure_with(struct video_desc desc) m_encoder_param.sampling_factor[2].vertical = 1; m_encoder_param.interleaved = (codec_is_a_rgb(m_enc_input_codec) && !m_parent_state->m_force_interleaved) ? 0 : 1; - - m_encoder_param.color_space_internal = m_parent_state->m_use_internal_codec == RGB || codec_is_a_rgb(m_enc_input_codec) - ? GPUJPEG_RGB : GPUJPEG_YCBCR_BT709; + if (m_parent_state->m_use_internal_codec == GPUJPEG_NONE) { + m_encoder_param.color_space_internal = codec_is_a_rgb(m_enc_input_codec) + ? GPUJPEG_RGB : GPUJPEG_YCBCR_BT709; + } else { + m_encoder_param.color_space_internal = m_parent_state->m_use_internal_codec; + } gpujpeg_image_set_default_parameters(&m_param_image); @@ -388,11 +389,15 @@ bool state_video_compress_gpujpeg::parse_fmt(char *fmt) m_quality = atoi(tok + strlen("restart=")); } else if (strcasecmp(tok, "interleaved") == 0) { m_force_interleaved = true; - } else if (strcasecmp(tok, "YUV") == 0) { - m_use_internal_codec = UYVY; + } else if (strcasecmp(tok, "Y601") == 0) { + m_use_internal_codec = GPUJPEG_YCBCR_BT601; + } else if (strcasecmp(tok, "Y601full") == 0) { + m_use_internal_codec = GPUJPEG_YCBCR_BT601_256LVLS; + } else if (strcasecmp(tok, "Y709") == 0) { + m_use_internal_codec = GPUJPEG_YCBCR_BT709; } else if (strcasecmp(tok, "RGB") == 0) { #if LIBGPUJPEG_API_VERSION >= 4 - m_use_internal_codec = RGB; + m_use_internal_codec = GPUJPEG_RGB; #else log_msg(LOG_LEVEL_ERROR, "[GPUJPEG] Cannot use RGB as an internal colorspace (old GPUJPEG).\n"); return false; @@ -474,7 +479,7 @@ struct module * gpujpeg_compress_init(struct module *parent, const char *opts) if(opts && strcmp(opts, "help") == 0) { cout << "GPUJPEG comperssion usage:\n"; - cout << "\t" << BOLD(RED("-c GPUJPEG") << "[:<quality>[:<restart_interval>]][:interleaved][:RGB|:YUV][:subsampling=<sub>]\n"); + cout << "\t" << BOLD(RED("-c GPUJPEG") << "[:<quality>[:<restart_interval>]][:interleaved][:RGB|Y601|Y601full|Y709]][:subsampling=<sub>]\n"); cout << "where\n"; cout << BOLD("\tquality\n") << "\t\tJPEG quality coefficient [0..100] - more is better\n"; @@ -489,9 +494,8 @@ struct module * gpujpeg_compress_init(struct module *parent, const char *opts) "\t\tNon-interleaved has slightly better performance for RGB at the\n" "\t\texpense of worse compatibility. Therefore this option may be\n" "\t\tenabled safely.\n"; - cout << BOLD("\tRGB|YUV\n") << - "\t\tforce RGB or YUV as an internal JPEG color space (otherwise\n" - "\t\tsource color space is kept).\n"; + cout << BOLD("\tRGB|Y601|Y601full|Y709\n") << + "\t\tforce internal JPEG color space (otherwise source color space is kept).\n"; cout << BOLD("\t<sub>\n") << "\t\tUse specified JPEG subsampling (444, 422 or 420).\n"; cout << "\n"; From 177a6973efc1bde5c5b185176e41bd167ab32310 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Fri, 2 Oct 2020 10:15:31 +0200 Subject: [PATCH 71/74] GPUJPEG dec.: fixes --- src/video_decompress/gpujpeg.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/video_decompress/gpujpeg.c b/src/video_decompress/gpujpeg.c index 789196fdf..651e825df 100644 --- a/src/video_decompress/gpujpeg.c +++ b/src/video_decompress/gpujpeg.c @@ -95,12 +95,17 @@ static int configure_with(struct state_decompress_gpujpeg *s, struct video_desc return FALSE; } + // setting verbosity - a bit tricky now, gpujpeg_decoder_init needs to be called with some "valid" data + // otherwise, parameter setting is unneeded - it is done automaticaly by the image struct gpujpeg_parameters param; gpujpeg_set_default_parameters(¶m); param.verbose = MAX(0, log_level - LOG_LEVEL_INFO); struct gpujpeg_image_parameters param_image; gpujpeg_image_set_default_parameters(¶m_image); - gpujpeg_decoder_init(s->decoder, ¶m, ¶m_image); + param_image.width = desc.width; // size must be non-zero in order the init to succeed + param_image.height = desc.height; + int rc = gpujpeg_decoder_init(s->decoder, ¶m, ¶m_image); + assert(rc == 0); switch (s->out_codec) { case I420: From 1ca6a968ed5f7083dab8d6882d86fefecdacbc22 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Fri, 2 Oct 2020 10:28:22 +0200 Subject: [PATCH 72/74] GPUJPEG dec.: set BT.709 by default FFmpeg generated ones doesn't have JFIF headers so this will be assumed default. --- src/video_decompress/gpujpeg.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/video_decompress/gpujpeg.c b/src/video_decompress/gpujpeg.c index 651e825df..4ea171642 100644 --- a/src/video_decompress/gpujpeg.c +++ b/src/video_decompress/gpujpeg.c @@ -104,6 +104,9 @@ static int configure_with(struct state_decompress_gpujpeg *s, struct video_desc gpujpeg_image_set_default_parameters(¶m_image); param_image.width = desc.width; // size must be non-zero in order the init to succeed param_image.height = desc.height; + param_image.color_space = GPUJPEG_YCBCR_BT709; // assume now BT.709 as default - this is mainly applicable for FFmpeg-encoded + // JPEGs that doesn't indicate explicitly color spec (no JFIF marker, only CS=ITU601 + // for BT.601 limited range - not enabled by UG encoder because FFmpeg emits it also for 709) int rc = gpujpeg_decoder_init(s->decoder, ¶m, ¶m_image); assert(rc == 0); From 9bebe25ea87532185ccf2d2e69261c67bbbf026b Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Fri, 2 Oct 2020 10:52:00 +0200 Subject: [PATCH 73/74] Lavc: removed setting of range after init FFmpeg actually sets the CS=ITU601 dubiously - it writes it even for BT.701 limited range which than can lead to confusion on the decoder (especially GPUJPEG) how to interpret. Therefore, we "utilize" the bug that avcodec_open2() overrides our values for YUVJ formats and doesn't emit dubious CS=ITU601 COM marker. --- src/video_compress/libavcodec.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/video_compress/libavcodec.cpp b/src/video_compress/libavcodec.cpp index 0738c6297..1ecce00c9 100644 --- a/src/video_compress/libavcodec.cpp +++ b/src/video_compress/libavcodec.cpp @@ -985,14 +985,6 @@ static bool try_open_codec(struct state_video_compress_libav *s, } pthread_mutex_unlock(s->lavcd_global_lock); - // setting again for JPEG encoder because avcodec_open2 overrides color_range from MPEG to JPEG for YUVJ* pix_fmts - if (pix_fmt == AV_PIX_FMT_YUVJ420P || pix_fmt == AV_PIX_FMT_YUVJ411P || pix_fmt == AV_PIX_FMT_YUVJ422P || pix_fmt == AV_PIX_FMT_YUVJ440P || pix_fmt == AV_PIX_FMT_YUVJ444P) { - if (const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get(pix_fmt)) { // defaults - s->codec_ctx->colorspace = (desc->flags & AV_PIX_FMT_FLAG_RGB) != 0U ? AVCOL_SPC_RGB : AVCOL_SPC_BT709; - s->codec_ctx->color_range = (desc->flags & AV_PIX_FMT_FLAG_RGB) != 0U ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; - } - get_av_pixfmt_details(ug_codec, pix_fmt, &s->codec_ctx->colorspace, &s->codec_ctx->color_range); - } return true; } From 411563f10bbb54cb139b6568202bd5ac4060a1c9 Mon Sep 17 00:00:00 2001 From: Martin Pulec <martin.pulec@cesnet.cz> Date: Fri, 2 Oct 2020 14:41:46 +0200 Subject: [PATCH 74/74] GPUJPEG: remove compat code Require API 11. --- src/video_compress/gpujpeg.cpp | 37 ++----------------- src/video_decompress/gpujpeg.c | 67 +++++----------------------------- 2 files changed, 14 insertions(+), 90 deletions(-) diff --git a/src/video_compress/gpujpeg.cpp b/src/video_compress/gpujpeg.cpp index f864af11a..49b6ace52 100644 --- a/src/video_compress/gpujpeg.cpp +++ b/src/video_compress/gpujpeg.cpp @@ -63,25 +63,14 @@ #include <set> #include <vector> +#if LIBGPUJPEG_API_VERSION < 11 +#error "GPUJPEG API 10 or more requested!" +#endif + #define MOD_NAME "[GPUJPEG enc.] " using namespace std; -#if LIBGPUJPEG_API_VERSION >= 7 -#define GJ_RGBA_SUPP 1 -#else -#define GJ_RGBA_SUPP 0 -#endif - -// compat -#if LIBGPUJPEG_API_VERSION <= 2 -#define GPUJPEG_444_U8_P012 GPUJPEG_4_4_4 -#define GPUJPEG_422_U8_P1020 GPUJPEG_4_2_2 -#endif -#if LIBGPUJPEG_API_VERSION < 7 -#define GPUJPEG_YCBCR_JPEG GPUJPEG_YCBCR_BT601_256LVLS -#endif - namespace { struct state_video_compress_gpujpeg; @@ -327,13 +316,10 @@ bool encoder_state::configure_with(struct video_desc desc) m_param_image.comp_count = 3; m_param_image.color_space = codec_is_a_rgb(m_enc_input_codec) ? GPUJPEG_RGB : GPUJPEG_YCBCR_BT709; -#if LIBGPUJPEG_API_VERSION > 2 switch (m_enc_input_codec) { case I420: m_param_image.pixel_format = GPUJPEG_420_U8_P0P1P2; break; case RGB: m_param_image.pixel_format = GPUJPEG_444_U8_P012; break; -#if GJ_RGBA_SUPP == 1 case RGBA: m_param_image.pixel_format = GPUJPEG_444_U8_P012Z; break; -#endif case UYVY: m_param_image.pixel_format = GPUJPEG_422_U8_P1020; break; default: log_msg(LOG_LEVEL_FATAL, MOD_NAME "Unexpected codec: %s\n", @@ -341,10 +327,6 @@ bool encoder_state::configure_with(struct video_desc desc) abort(); } m_encoder = gpujpeg_encoder_create(NULL); -#else - m_param_image.sampling_factor = m_enc_input_codec == RGB ? GPUJPEG_4_4_4 : GPUJPEG_4_2_2; - m_encoder = gpujpeg_encoder_create(&m_encoder_param, &m_param_image); -#endif int data_len = desc.width * desc.height * 3; m_pool.reconfigure(compressed_desc, data_len); @@ -396,12 +378,7 @@ bool state_video_compress_gpujpeg::parse_fmt(char *fmt) } else if (strcasecmp(tok, "Y709") == 0) { m_use_internal_codec = GPUJPEG_YCBCR_BT709; } else if (strcasecmp(tok, "RGB") == 0) { -#if LIBGPUJPEG_API_VERSION >= 4 m_use_internal_codec = GPUJPEG_RGB; -#else - log_msg(LOG_LEVEL_ERROR, "[GPUJPEG] Cannot use RGB as an internal colorspace (old GPUJPEG).\n"); - return false; -#endif } else if (strstr(tok, "subsampling=") == tok) { m_subsampling = atoi(tok + strlen("subsampling=")); assert(set<int>({444, 422, 420}).count(m_subsampling) == 1); @@ -469,12 +446,10 @@ state_video_compress_gpujpeg *state_video_compress_gpujpeg::create(struct module struct module * gpujpeg_compress_init(struct module *parent, const char *opts) { -#if LIBGPUJPEG_API_VERSION >= 7 if (gpujpeg_version() != LIBGPUJPEG_API_VERSION) { LOG(LOG_LEVEL_WARNING) << "GPUJPEG API version mismatch! (" << gpujpeg_version() << " vs " << LIBGPUJPEG_API_VERSION << ")\n"; } -#endif struct state_video_compress_gpujpeg *s; if(opts && strcmp(opts, "help") == 0) { @@ -580,11 +555,7 @@ shared_ptr<video_frame> encoder_state::compress_step(shared_ptr<video_frame> tx) struct gpujpeg_encoder_input encoder_input; gpujpeg_encoder_input_set_image(&encoder_input, jpeg_enc_input_data); -#if LIBGPUJPEG_API_VERSION <= 2 - ret = gpujpeg_encoder_encode(m_encoder, &encoder_input, &compressed, &size); -#else ret = gpujpeg_encoder_encode(m_encoder, &m_encoder_param, &m_param_image, &encoder_input, &compressed, &size); -#endif if(ret != 0) { return {}; diff --git a/src/video_decompress/gpujpeg.c b/src/video_decompress/gpujpeg.c index 4ea171642..8199f9aee 100644 --- a/src/video_decompress/gpujpeg.c +++ b/src/video_decompress/gpujpeg.c @@ -52,20 +52,8 @@ #include <stdlib.h> #include "lib_common.h" -#if LIBGPUJPEG_API_VERSION >= 7 -#define GJ_RGBA_SUPP 1 -#else -#define GJ_RGBA_SUPP 0 -#endif - -// compat -#if LIBGPUJPEG_API_VERSION <= 2 -#define GPUJPEG_444_U8_P012 GPUJPEG_4_4_4 -#define GPUJPEG_422_U8_P1020 GPUJPEG_4_2_2 -#endif - -#if LIBGPUJPEG_API_VERSION < 9 -#define GPUJPEG_444_U8_P012A GPUJPEG_444_U8_P012Z +#if LIBGPUJPEG_API_VERSION < 11 +#error "GPUJPEG API 11 or more requested!" #endif #define MOD_NAME "[GPUJPEG dec.] " @@ -85,12 +73,7 @@ static int configure_with(struct state_decompress_gpujpeg *s, struct video_desc { s->desc = desc; - -#if LIBGPUJPEG_API_VERSION <= 2 - s->decoder = gpujpeg_decoder_create(); -#else s->decoder = gpujpeg_decoder_create(NULL); -#endif if(!s->decoder) { return FALSE; } @@ -116,12 +99,10 @@ static int configure_with(struct state_decompress_gpujpeg *s, struct video_desc GPUJPEG_420_U8_P0P1P2); break; case RGBA: -#if GJ_RGBA_SUPP == 1 gpujpeg_decoder_set_output_format(s->decoder, GPUJPEG_RGB, s->out_codec == RGBA && s->rshift == 0 && s->gshift == 8 && s->bshift == 16 && vc_get_linesize(desc.width, RGBA) == s->pitch ? GPUJPEG_444_U8_P012A : GPUJPEG_444_U8_P012); break; -#endif case RGB: gpujpeg_decoder_set_output_format(s->decoder, GPUJPEG_RGB, GPUJPEG_444_U8_P012); @@ -141,15 +122,12 @@ static int configure_with(struct state_decompress_gpujpeg *s, struct video_desc static void * gpujpeg_decompress_init(void) { -#if LIBGPUJPEG_API_VERSION >= 7 if (gpujpeg_version() != LIBGPUJPEG_API_VERSION) { log_msg(LOG_LEVEL_WARNING, "GPUJPEG API version mismatch! (%d vs %d)\n", gpujpeg_version(), LIBGPUJPEG_API_VERSION); } -#endif - struct state_decompress_gpujpeg *s; - s = (struct state_decompress_gpujpeg *) calloc(1, sizeof(struct state_decompress_gpujpeg)); + struct state_decompress_gpujpeg *s = (struct state_decompress_gpujpeg *) calloc(1, sizeof(struct state_decompress_gpujpeg)); int ret; printf("Initializing CUDA device %d...\n", cuda_devices[0]); @@ -192,19 +170,10 @@ static int gpujpeg_decompress_reconfigure(void *state, struct video_desc desc, } } -#if LIBGPUJPEG_API_VERSION >= 4 static decompress_status gpujpeg_probe_internal_codec(unsigned char *buffer, size_t len, codec_t *internal_codec) { *internal_codec = VIDEO_CODEC_NONE; struct gpujpeg_image_parameters params = { 0 }; -#if LIBGPUJPEG_API_VERSION >= 11 if (gpujpeg_decoder_get_image_info(buffer, len, ¶ms, NULL, MAX(0, log_level - LOG_LEVEL_INFO)) != 0) { -#elif LIBGPUJPEG_API_VERSION >= 6 - if (gpujpeg_decoder_get_image_info(buffer, len, ¶ms, NULL) != 0) { -#elif LIBGPUJPEG_API_VERSION >= 5 - if (gpujpeg_reader_get_image_info(buffer, len, ¶ms, NULL) != 0) { -#else - if (gpujpeg_decoder_get_image_info(buffer, len, ¶ms) != 0) { -#endif log_msg(LOG_LEVEL_WARNING, MOD_NAME "probe - cannot get image info!\n"); return DECODER_GOT_FRAME; } @@ -221,11 +190,7 @@ static decompress_status gpujpeg_probe_internal_codec(unsigned char *buffer, siz case GPUJPEG_YCBCR_BT601: case GPUJPEG_YCBCR_BT601_256LVLS: case GPUJPEG_YCBCR_BT709: -#if LIBGPUJPEG_API_VERSION < 8 - *internal_codec = UYVY; -#else *internal_codec = params.pixel_format == GPUJPEG_420_U8_P0P1P2 ? I420 : UYVY; -#endif break; default: log_msg(LOG_LEVEL_WARNING, MOD_NAME "probe - unhandled color space: %s\n", @@ -236,7 +201,6 @@ static decompress_status gpujpeg_probe_internal_codec(unsigned char *buffer, siz log_msg(LOG_LEVEL_VERBOSE, "JPEG color space: %s\n", gpujpeg_color_space_get_name(params.color_space)); return DECODER_GOT_CODEC; } -#endif static decompress_status gpujpeg_decompress(void *state, unsigned char *dst, unsigned char *buffer, unsigned int src_len, int frame_seq, struct video_frame_callbacks *callbacks, codec_t *internal_codec) @@ -249,11 +213,7 @@ static decompress_status gpujpeg_decompress(void *state, unsigned char *dst, uns int linesize; if (s->out_codec == VIDEO_CODEC_NONE) { -#if LIBGPUJPEG_API_VERSION >= 4 return gpujpeg_probe_internal_codec(buffer, src_len, internal_codec); -#else - assert("Old GPUJPEG, cannot probe!" && 0); -#endif } linesize = vc_get_linesize(s->desc.width, s->out_codec); @@ -261,9 +221,7 @@ static decompress_status gpujpeg_decompress(void *state, unsigned char *dst, uns gpujpeg_set_device(cuda_devices[0]); if (s->pitch == linesize && (s->out_codec == UYVY || s->out_codec == RGB -#if GJ_RGBA_SUPP == 1 || (s->out_codec == RGBA && s->rshift == 0 && s->gshift == 8 && s->bshift == 16) -#endif )) { gpujpeg_decoder_output_set_custom(&decoder_output, dst); //int data_decompressed_size = decoder_output.data_size; @@ -333,38 +291,33 @@ static void gpujpeg_decompress_done(void *state) static const struct decode_from_to *gpujpeg_decompress_get_decoders() { static const struct decode_from_to ret[] = { -#if LIBGPUJPEG_API_VERSION >= 4 - { JPEG, VIDEO_CODEC_NONE, VIDEO_CODEC_NONE, 50 }, -#endif + { JPEG, VIDEO_CODEC_NONE, VIDEO_CODEC_NONE, 50 }, // for probe { JPEG, RGB, RGB, 300 }, - { JPEG, RGB, RGBA, 300 + (1 - GJ_RGBA_SUPP) * 50 }, // 300 when GJ support RGBA natively, - // 350 when using CPU conversion + { JPEG, RGB, RGBA, 300 }, { JPEG, UYVY, UYVY, 300 }, { JPEG, I420, I420, 300 }, { JPEG, I420, UYVY, 500 }, { JPEG, RGB, UYVY, 700 }, { JPEG, UYVY, RGB, 700 }, - { JPEG, UYVY, RGBA, 700 + (1 - GJ_RGBA_SUPP) * 50}, + { JPEG, UYVY, RGBA, 700 }, { JPEG, VIDEO_CODEC_NONE, RGB, 900 }, { JPEG, VIDEO_CODEC_NONE, UYVY, 900 }, - { JPEG, VIDEO_CODEC_NONE, RGBA, 900 + (1 - GJ_RGBA_SUPP) * 50}, -#if LIBGPUJPEG_API_VERSION > 6 + { JPEG, VIDEO_CODEC_NONE, RGBA, 900 }, // decoding from FFmpeg MJPG has lower priority than libavcodec // decoder because those files doesn't has much independent // segments (1 per MCU row -> 68 for HD) -> lavd may be better { MJPG, VIDEO_CODEC_NONE, VIDEO_CODEC_NONE, 90 }, { MJPG, RGB, RGB, 600 }, - { MJPG, RGB, RGBA, 600 + (1 - GJ_RGBA_SUPP) * 50 }, + { MJPG, RGB, RGBA, 600 }, { MJPG, UYVY, UYVY, 600 }, { MJPG, I420, I420, 600 }, { MJPG, I420, UYVY, 700 }, { MJPG, RGB, UYVY, 800 }, { MJPG, UYVY, RGB, 800 }, - { MJPG, UYVY, RGBA, 800 + (1 - GJ_RGBA_SUPP) * 50}, + { MJPG, UYVY, RGBA, 800 }, { MJPG, VIDEO_CODEC_NONE, RGB, 920 }, { MJPG, VIDEO_CODEC_NONE, UYVY, 920 }, - { MJPG, VIDEO_CODEC_NONE, RGBA, 920 + (1 - GJ_RGBA_SUPP) * 50}, -#endif + { MJPG, VIDEO_CODEC_NONE, RGBA, 920 }, { VIDEO_CODEC_NONE, VIDEO_CODEC_NONE, VIDEO_CODEC_NONE, 0 }, }; return ret;