mirror of
https://github.com/outbackdingo/UltraGrid.git
synced 2026-03-21 16:40:18 +00:00
Merge branch 'trunk' of http://seth.ics.muni.cz/git/ultragrid into I2CAT's trunk
This commit is contained in:
11
Makefile.in
11
Makefile.in
@@ -8,7 +8,7 @@ LINKER = @LINKER@
|
||||
COMMON_FLAGS = -g @DEFS@ -DPATH_PREFIX=@prefix@ -DLIB_DIR=@libdir@ -Wall -Wextra -Wpointer-arith -msse2
|
||||
CFLAGS = @CFLAGS@ @X_CFLAGS@ $(COMMON_FLAGS) -D_GNU_SOURCE
|
||||
CPPFLAGS = @CPPFLAGS@ -D_GNU_SOURCE
|
||||
CXXFLAGS = @CXXFLAGS@ $(COMMON_FLAGS) -D_GNU_SOURCE
|
||||
CXXFLAGS = @CXXFLAGS@ $(COMMON_FLAGS) -D_GNU_SOURCE
|
||||
NVCCFLAGS = @NVCCFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS += @LIBS@ @JACK_TRANS_LIB@ @MATHLIBS@ @COREAUDIO_LIB@ \
|
||||
@@ -125,6 +125,7 @@ OBJS = @OBJS@ \
|
||||
src/video_capture/aggregate.o \
|
||||
src/video_capture/import.o \
|
||||
src/video_capture/null.o \
|
||||
src/video_capture/switcher.o \
|
||||
src/video_compress.o \
|
||||
src/video_compress/none.o \
|
||||
src/video_decompress.o \
|
||||
@@ -157,9 +158,10 @@ REFLECTOR_OBJS = src/hd-rum-translator/hd-rum-decompress.o \
|
||||
src/hd-rum-translator/hd-rum-translator.o
|
||||
|
||||
IMPORT_CONTROL_KEYBOARD_OBJS = src/import_control_keyboard.o
|
||||
SWITCHER_CONTROL_KEYBOARD_OBJS = src/switcher_control_keyboard.o
|
||||
|
||||
# -------------------------------------------------------------------------------------------------
|
||||
all: $(TARGET) $(GUI_TARGET) bin/import_control_keyboard $(REFLECTOR_TARGET) modules ag-plugins configure-messages
|
||||
all: $(TARGET) $(GUI_TARGET) bin/import_control_keyboard bin/switcher_control_keyboard $(REFLECTOR_TARGET) modules ag-plugins configure-messages
|
||||
|
||||
modules: @LIB_TARGETS@
|
||||
|
||||
@@ -171,7 +173,10 @@ $(TARGET): bindir $(OBJS) $(ULTRAGRID_OBJS) $(GENERATED_HEADERS)
|
||||
if [ -n "@DLL_LIBS@" ]; then $(INSTALL) -m 644 @DLL_LIBS@ bin; fi
|
||||
|
||||
bin/import_control_keyboard: bindir $(IMPORT_CONTROL_KEYBOARD_OBJS)
|
||||
$(LINKER) $(LDFLAGS) $(IMPORT_CONTROL_KEYBOARD_OBJS) @IMPORT_CONTROL_KEYBOARD_LIBS@ -o $@
|
||||
$(LINKER) $(LDFLAGS) $(IMPORT_CONTROL_KEYBOARD_OBJS) @NCURSES_LIBS@ -o $@
|
||||
|
||||
bin/switcher_control_keyboard: bindir $(SWITCHER_CONTROL_KEYBOARD_OBJS)
|
||||
$(LINKER) $(LDFLAGS) $(SWITCHER_CONTROL_KEYBOARD_OBJS) @NCURSES_LIBS@ -o $@ -pthread
|
||||
|
||||
$(REFLECTOR_TARGET): bindir $(OBJS) $(GENERATED_HEADERS) $(REFLECTOR_OBJS)
|
||||
$(LINKER) $(LDFLAGS) $(OBJS) $(REFLECTOR_OBJS) $(LIBS) -o $@
|
||||
|
||||
@@ -2548,7 +2548,6 @@ then
|
||||
SAVED_LIBS=$LIBS
|
||||
LIBAVCODEC_LIBS="$LIBAVCODEC_LIBS $LIBAVUTIL_LIBS" # append libavutil
|
||||
LIBS="$LIBS $LIBAVCODEC_LIBS"
|
||||
AC_CHECK_FUNCS(avcodec_encode_video2)
|
||||
LIBS=$SAVED_LIBS
|
||||
else
|
||||
LIBAVCODEC_LIBS=
|
||||
@@ -2628,11 +2627,10 @@ if test $system = Linux -o $system = MacOSX; then
|
||||
|
||||
if test $FOUND_NCURSES = yes; then
|
||||
AC_DEFINE([HAVE_NCURSES], [1], [Build with ncurses support])
|
||||
IMPORT_CONTROL_KEYBOARD_LIBS="$NCURSES_LIBS"
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_SUBST(IMPORT_CONTROL_KEYBOARD_LIBS)
|
||||
AC_SUBST(NCURSES_LIBS)
|
||||
|
||||
# -------------------------------------------------------------------------------------------------
|
||||
# OpenSSL-libcrypto
|
||||
|
||||
BIN
share/logo_cesnet.pam
Normal file
BIN
share/logo_cesnet.pam
Normal file
Binary file not shown.
@@ -144,6 +144,8 @@ struct state_audio {
|
||||
int resample_to;
|
||||
|
||||
char *requested_encryption;
|
||||
|
||||
volatile bool paused;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -196,13 +198,15 @@ struct state_audio * audio_cfg_init(struct module *parent, const char *addrs, in
|
||||
const char *send_cfg, const char *recv_cfg,
|
||||
char *jack_cfg, const char *fec_cfg, const char *encryption,
|
||||
char *audio_channel_map, const char *audio_scale,
|
||||
bool echo_cancellation, bool use_ipv6, const char *mcast_if, audio_codec_t audio_codec,
|
||||
int resample_to, bool isStd, long packet_rate)
|
||||
bool echo_cancellation, bool use_ipv6, const char *mcast_if,
|
||||
const char *audio_codec_cfg,
|
||||
bool isStd, long packet_rate)
|
||||
{
|
||||
struct state_audio *s = NULL;
|
||||
char *tmp, *unused = NULL;
|
||||
UNUSED(unused);
|
||||
char *addr;
|
||||
int resample_to = get_audio_codec_sample_rate(audio_codec_cfg);
|
||||
|
||||
audio_capture_init_devices();
|
||||
audio_playback_init_devices();
|
||||
@@ -256,7 +260,7 @@ struct state_audio * audio_cfg_init(struct module *parent, const char *addrs, in
|
||||
s->audio_sender_thread_started = s->audio_receiver_thread_started = false;
|
||||
s->resample_to = resample_to;
|
||||
|
||||
s->audio_coder = audio_codec_init(audio_codec, AUDIO_CODER);
|
||||
s->audio_coder = audio_codec_init_cfg(audio_codec_cfg, AUDIO_CODER);
|
||||
if(!s->audio_coder) {
|
||||
goto error;
|
||||
}
|
||||
@@ -514,6 +518,8 @@ static void *audio_receiver_thread(void *arg)
|
||||
while (!should_exit_audio) {
|
||||
bool decoded = false;
|
||||
|
||||
pbuf_data.buffer.data_len = 0;
|
||||
|
||||
if(s->receiver == NET_NATIVE) {
|
||||
gettimeofday(&curr_time, NULL);
|
||||
ts = tv_diff(curr_time, s->start_time) * 90000;
|
||||
@@ -527,7 +533,10 @@ static void *audio_receiver_thread(void *arg)
|
||||
cp = pdb_iter_init(s->audio_participants, &it);
|
||||
|
||||
while (cp != NULL) {
|
||||
if (audio_pbuf_decode(cp->playout_buffer, curr_time, decode_audio_frame, &pbuf_data)) {
|
||||
// We iterate in loop since there can be more than one frmae present in
|
||||
// the playout buffer and it would be discarded by following pbuf_remove()
|
||||
// call.
|
||||
while (pbuf_decode(cp->playout_buffer, curr_time, decode_audio_frame, &pbuf_data)) {
|
||||
decoded = true;
|
||||
}
|
||||
|
||||
@@ -552,7 +561,8 @@ static void *audio_receiver_thread(void *arg)
|
||||
cp = pdb_iter_init(s->audio_participants, &it);
|
||||
|
||||
while (cp != NULL) {
|
||||
if (audio_pbuf_decode(cp->playout_buffer, curr_time, decode_audio_frame_mulaw, &pbuf_data)) {
|
||||
// should be perhaps run iteratively? similarly to NET_NATIVE
|
||||
if (pbuf_decode(cp->playout_buffer, curr_time, decode_audio_frame_mulaw, &pbuf_data)) {
|
||||
bool failed = false;
|
||||
if(s->echo_state) {
|
||||
#ifdef HAVE_SPEEX
|
||||
@@ -733,9 +743,15 @@ static void audio_sender_process_message(struct state_audio *s, struct msg_sende
|
||||
&s->audio_network_parameters);
|
||||
break;
|
||||
case SENDER_MSG_PAUSE:
|
||||
s->paused = true;
|
||||
break;
|
||||
case SENDER_MSG_PLAY:
|
||||
s->paused = false;
|
||||
break;
|
||||
case SENDER_MSG_CHANGE_FEC:
|
||||
fprintf(stderr, "Not implemented!\n");
|
||||
abort();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -770,6 +786,9 @@ static void *audio_sender_thread(void *arg)
|
||||
continue;
|
||||
#endif
|
||||
}
|
||||
if (s->paused) {
|
||||
continue;
|
||||
}
|
||||
if(s->sender == NET_NATIVE) {
|
||||
// RESAMPLE
|
||||
resample(&resample_state, buffer);
|
||||
|
||||
@@ -126,8 +126,8 @@ struct state_audio * audio_cfg_init(struct module *parent, const char *addrs, in
|
||||
const char *send_cfg, const char *recv_cfg,
|
||||
char *jack_cfg, const char *fec_cfg, const char *encryption,
|
||||
char *audio_channel_map, const char *audio_scale,
|
||||
bool echo_cancellation, bool use_ipv6, const char *mcast_iface, audio_codec_t audio_codec,
|
||||
int resample_to, bool isStd, long packet_rate);
|
||||
bool echo_cancellation, bool use_ipv6, const char *mcast_iface, const char *audio_codec_cfg,
|
||||
bool isStd, long packet_rate);
|
||||
void audio_finish(void);
|
||||
void audio_done(struct state_audio *s);
|
||||
void audio_join(struct state_audio *s);
|
||||
|
||||
@@ -203,8 +203,6 @@ void * audio_cap_jack_init(char *cfg)
|
||||
|
||||
s->data = ring_buffer_init(s->frame.max_size);
|
||||
|
||||
free(ports);
|
||||
|
||||
if(jack_set_sample_rate_callback(s->client, jack_samplerate_changed_callback, (void *) s)) {
|
||||
fprintf(stderr, "[JACK capture] Registring callback problem.\n");
|
||||
goto release_client;
|
||||
@@ -234,6 +232,8 @@ void * audio_cap_jack_init(char *cfg)
|
||||
}
|
||||
}
|
||||
|
||||
free(ports);
|
||||
|
||||
return s;
|
||||
|
||||
release_client:
|
||||
|
||||
@@ -87,7 +87,7 @@ static struct audio_codec *audio_codecs[MAX_AUDIO_CODECS] = {
|
||||
&dummy_pcm_audio_codec,
|
||||
NULL_IF_BUILD_LIBRARIES(LIBAVCODEC_AUDIO_CODEC_HANDLE),
|
||||
};
|
||||
static struct audio_codec_state *audio_codec_init_real(audio_codec_t audio_codec,
|
||||
static struct audio_codec_state *audio_codec_init_real(const char *audio_codec_cfg,
|
||||
audio_codec_direction_t direction, bool try_init);
|
||||
static void register_audio_codec_real(struct audio_codec *);
|
||||
|
||||
@@ -111,17 +111,20 @@ struct audio_codec_state {
|
||||
audio_codec_t codec;
|
||||
audio_codec_direction_t direction;
|
||||
audio_frame2 *out;
|
||||
int bitrate;
|
||||
};
|
||||
|
||||
void list_audio_codecs(void) {
|
||||
printf("Syntax:\n");
|
||||
printf("\t--audio-codec <audio_codec>[:<sampling_rate>]\n");
|
||||
printf("\t--audio-codec <audio_codec>[:sample_rate=<sampling_rate>][:bitrate=<bitrate>]\n");
|
||||
printf("\n");
|
||||
printf("Supported audio codecs:\n");
|
||||
for(int i = 0; i < audio_codec_info_len; ++i) {
|
||||
if(i != AC_NONE) {
|
||||
printf("\t%s", audio_codec_info[i].name);
|
||||
struct audio_codec_state *st = audio_codec_init_real(i, AUDIO_CODER, true);
|
||||
struct audio_codec_state *st = (struct audio_codec_state *)
|
||||
audio_codec_init_real(get_name_to_audio_codec((audio_codec_t) i),
|
||||
AUDIO_CODER, true);
|
||||
if(!st) {
|
||||
printf(" - unavailable");
|
||||
} else {
|
||||
@@ -145,11 +148,19 @@ static void load_libraries(void)
|
||||
|
||||
struct audio_codec_state *audio_codec_init(audio_codec_t audio_codec,
|
||||
audio_codec_direction_t direction) {
|
||||
return audio_codec_init_real(audio_codec, direction, true);
|
||||
return audio_codec_init_real(get_name_to_audio_codec(audio_codec), direction, true);
|
||||
}
|
||||
|
||||
static struct audio_codec_state *audio_codec_init_real(audio_codec_t audio_codec,
|
||||
struct audio_codec_state *audio_codec_init_cfg(const char *audio_codec_cfg,
|
||||
audio_codec_direction_t direction) {
|
||||
return audio_codec_init_real(audio_codec_cfg, direction, true);
|
||||
}
|
||||
|
||||
|
||||
static struct audio_codec_state *audio_codec_init_real(const char *audio_codec_cfg,
|
||||
audio_codec_direction_t direction, bool try_init) {
|
||||
audio_codec_t audio_codec = get_audio_codec(audio_codec_cfg);
|
||||
int bitrate = get_audio_codec_bitrate(audio_codec_cfg);
|
||||
void *state = NULL;
|
||||
int index;
|
||||
#ifdef BUILD_LIBRARIES
|
||||
@@ -160,7 +171,7 @@ static struct audio_codec_state *audio_codec_init_real(audio_codec_t audio_codec
|
||||
continue;
|
||||
for(unsigned int j = 0; audio_codecs[i]->supported_codecs[j] != AC_NONE; ++j) {
|
||||
if(audio_codecs[i]->supported_codecs[j] == audio_codec) {
|
||||
state = audio_codecs[i]->init(audio_codec, direction, try_init);
|
||||
state = audio_codecs[i]->init(audio_codec, direction, try_init, bitrate);
|
||||
index = i;
|
||||
if(state) {
|
||||
break;
|
||||
@@ -185,12 +196,13 @@ static struct audio_codec_state *audio_codec_init_real(audio_codec_t audio_codec
|
||||
|
||||
struct audio_codec_state *s = (struct audio_codec_state *) malloc(sizeof(struct audio_codec_state));
|
||||
|
||||
s->state = calloc(1, sizeof(void**));
|
||||
s->state = (void **) calloc(1, sizeof(void**));
|
||||
s->state[0] = state;
|
||||
s->state_count = 1;
|
||||
s->index = index;
|
||||
s->codec = audio_codec;
|
||||
s->direction = direction;
|
||||
s->bitrate = bitrate;
|
||||
|
||||
s->out = audio_frame2_init();
|
||||
s->out->ch_count = 1;
|
||||
@@ -220,9 +232,9 @@ struct audio_codec_state *audio_codec_reconfigure(struct audio_codec_state *old,
|
||||
audio_frame2 *audio_codec_compress(struct audio_codec_state *s, audio_frame2 *frame)
|
||||
{
|
||||
if(frame && s->state_count < frame->ch_count) {
|
||||
s->state = realloc(s->state, sizeof(void **) * frame->ch_count);
|
||||
s->state = (void **) realloc(s->state, sizeof(void **) * frame->ch_count);
|
||||
for(int i = s->state_count; i < frame->ch_count; ++i) {
|
||||
s->state[i] = audio_codecs[s->index]->init(s->codec, s->direction, false);
|
||||
s->state[i] = audio_codecs[s->index]->init(s->codec, s->direction, false, s->bitrate);
|
||||
if(s->state[i] == NULL) {
|
||||
fprintf(stderr, "Error: initialization of audio codec failed!\n");
|
||||
return NULL;
|
||||
@@ -259,9 +271,9 @@ audio_frame2 *audio_codec_compress(struct audio_codec_state *s, audio_frame2 *fr
|
||||
audio_frame2 *audio_codec_decompress(struct audio_codec_state *s, audio_frame2 *frame)
|
||||
{
|
||||
if(s->state_count < frame->ch_count) {
|
||||
s->state = realloc(s->state, sizeof(void **) * frame->ch_count);
|
||||
s->state = (void **) realloc(s->state, sizeof(void **) * frame->ch_count);
|
||||
for(int i = s->state_count; i < frame->ch_count; ++i) {
|
||||
s->state[i] = audio_codecs[s->index]->init(s->codec, s->direction, false);
|
||||
s->state[i] = audio_codecs[s->index]->init(s->codec, s->direction, false, 0);
|
||||
if(s->state[i] == NULL) {
|
||||
fprintf(stderr, "Error: initialization of audio codec failed!\n");
|
||||
return NULL;
|
||||
@@ -317,3 +329,82 @@ const int *audio_codec_get_supported_bps(struct audio_codec_state *s)
|
||||
return audio_codecs[s->index]->supported_bytes_per_second;
|
||||
}
|
||||
|
||||
audio_codec_t get_audio_codec(const char *codec_str) {
|
||||
char *codec = strdup(codec_str);
|
||||
if (strchr(codec, ':')) {
|
||||
*strchr(codec, ':') = '\0';
|
||||
}
|
||||
for(int i = 0; i < audio_codec_info_len; ++i) {
|
||||
if(strcasecmp(audio_codec_info[i].name, codec) == 0) {
|
||||
free(codec);
|
||||
return (audio_codec_t) i;
|
||||
}
|
||||
}
|
||||
free(codec);
|
||||
return AC_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Caller must free() the returned buffer
|
||||
*/
|
||||
static char *get_val_from_cfg(const char *audio_codec_cfg, const char *key)
|
||||
{
|
||||
char *cfg = strdup(audio_codec_cfg);
|
||||
char *tmp = cfg;
|
||||
char *item, *save_ptr;
|
||||
|
||||
while ((item = strtok_r(cfg, ":", &save_ptr)) != NULL) {
|
||||
if (strncasecmp(key, item, strlen(key)) == 0) {
|
||||
free(tmp);
|
||||
return strdup(item + strlen(key));
|
||||
}
|
||||
cfg = NULL;
|
||||
}
|
||||
free(tmp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int get_audio_codec_sample_rate(const char *audio_codec_cfg)
|
||||
{
|
||||
char *val = get_val_from_cfg(audio_codec_cfg, "sample_rate=");
|
||||
if (val) {
|
||||
int ret = atoi(val);
|
||||
free(val);
|
||||
return ret;
|
||||
} else {
|
||||
return 48000;
|
||||
}
|
||||
}
|
||||
|
||||
int get_audio_codec_bitrate(const char *audio_codec_cfg)
|
||||
{
|
||||
char *val = get_val_from_cfg(audio_codec_cfg, "bitrate=");
|
||||
if (val) {
|
||||
int ret = atoi(val);
|
||||
free(val);
|
||||
return ret;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const char *get_name_to_audio_codec(audio_codec_t codec)
|
||||
{
|
||||
return audio_codec_info[codec].name;
|
||||
}
|
||||
|
||||
uint32_t get_audio_tag(audio_codec_t codec)
|
||||
{
|
||||
return audio_codec_info[codec].tag;
|
||||
}
|
||||
|
||||
audio_codec_t get_audio_codec_to_tag(uint32_t tag)
|
||||
{
|
||||
for(int i = 0; i < audio_codec_info_len; ++i) {
|
||||
if(audio_codec_info[i].tag == tag) {
|
||||
return (audio_codec_t) i;
|
||||
}
|
||||
}
|
||||
return AC_NONE;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ typedef enum {
|
||||
struct audio_codec {
|
||||
const audio_codec_t *supported_codecs;
|
||||
const int *supported_bytes_per_second;
|
||||
void *(*init)(audio_codec_t, audio_codec_direction_t, bool);
|
||||
void *(*init)(audio_codec_t, audio_codec_direction_t, bool, int bitrate);
|
||||
audio_channel *(*compress)(void *, audio_channel *);
|
||||
audio_channel *(*decompress)(void *, audio_channel *);
|
||||
void (*done)(void *);
|
||||
@@ -85,6 +85,7 @@ extern int audio_codec_info_len;
|
||||
struct audio_codec_state;
|
||||
|
||||
struct audio_codec_state *audio_codec_init(audio_codec_t audio_codec, audio_codec_direction_t);
|
||||
struct audio_codec_state *audio_codec_init_cfg(const char *audio_codec_cfg, audio_codec_direction_t);
|
||||
struct audio_codec_state *audio_codec_reconfigure(struct audio_codec_state *old,
|
||||
audio_codec_t audio_codec, audio_codec_direction_t);
|
||||
audio_frame2 *audio_codec_compress(struct audio_codec_state *, audio_frame2 *);
|
||||
@@ -94,6 +95,13 @@ void audio_codec_done(struct audio_codec_state *);
|
||||
|
||||
void list_audio_codecs(void);
|
||||
|
||||
audio_codec_t get_audio_codec(const char *audio_codec_cfg);
|
||||
int get_audio_codec_sample_rate(const char *audio_codec_cfg);
|
||||
int get_audio_codec_bitrate(const char *audio_codec_cfg);
|
||||
const char *get_name_to_audio_codec(audio_codec_t codec);
|
||||
uint32_t get_audio_tag(audio_codec_t codec);
|
||||
audio_codec_t get_audio_codec_to_tag(uint32_t audio_tag);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -60,7 +60,8 @@
|
||||
|
||||
#define MAGIC 0x552bca11
|
||||
|
||||
static void *dummy_pcm_init(audio_codec_t audio_codec, audio_codec_direction_t direction, bool try_init);
|
||||
static void *dummy_pcm_init(audio_codec_t audio_codec, audio_codec_direction_t direction, bool try_init,
|
||||
int bitrate);
|
||||
static audio_channel *dummy_pcm_compress(void *, audio_channel *);
|
||||
static audio_channel *dummy_pcm_decompress(void *, audio_channel *);
|
||||
static void dummy_pcm_done(void *);
|
||||
@@ -69,10 +70,12 @@ struct dummy_pcm_codec_state {
|
||||
uint32_t magic;
|
||||
};
|
||||
|
||||
static void *dummy_pcm_init(audio_codec_t audio_codec, audio_codec_direction_t direction, bool try_init)
|
||||
static void *dummy_pcm_init(audio_codec_t audio_codec, audio_codec_direction_t direction, bool try_init,
|
||||
int bitrate)
|
||||
{
|
||||
UNUSED(direction);
|
||||
UNUSED(try_init);
|
||||
UNUSED(bitrate);
|
||||
assert(audio_codec == AC_PCM);
|
||||
struct dummy_pcm_codec_state *s = malloc(sizeof(struct dummy_pcm_codec_state));
|
||||
s->magic = MAGIC;
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
#include "audio/codec/libavcodec.h"
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 55
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 54
|
||||
#include <libavutil/channel_layout.h>
|
||||
#endif
|
||||
#include <libavutil/mem.h>
|
||||
@@ -68,7 +68,7 @@
|
||||
|
||||
#define MAGIC 0xb135ca11
|
||||
|
||||
#ifndef HAVE_AVCODEC_ENCODE_VIDEO2
|
||||
#if LIBAVCODEC_VERSION_MAJOR < 54
|
||||
#define AV_CODEC_ID_PCM_ALAW CODEC_ID_PCM_ALAW
|
||||
#define AV_CODEC_ID_PCM_MULAW CODEC_ID_PCM_MULAW
|
||||
#define AV_CODEC_ID_ADPCM_IMA_WAV CODEC_ID_ADPCM_IMA_WAV
|
||||
@@ -79,7 +79,7 @@
|
||||
#endif
|
||||
|
||||
static void *libavcodec_init(audio_codec_t audio_codec, audio_codec_direction_t direction,
|
||||
bool try_init);
|
||||
bool try_init, int bitrate);
|
||||
static audio_channel *libavcodec_compress(void *, audio_channel *);
|
||||
static audio_channel *libavcodec_decompress(void *, audio_channel *);
|
||||
static void libavcodec_done(void *);
|
||||
@@ -101,7 +101,7 @@ static const audio_codec_t_to_codec_id_mapping_t mapping[] =
|
||||
[AC_MULAW] = { .codec_id = AV_CODEC_ID_PCM_MULAW },
|
||||
[AC_ADPCM_IMA_WAV] = { .codec_id = AV_CODEC_ID_ADPCM_IMA_WAV },
|
||||
[AC_SPEEX] = { .codec_id = AV_CODEC_ID_SPEEX },
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 55
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 54
|
||||
[AC_OPUS] = { .codec_id = AV_CODEC_ID_OPUS },
|
||||
#endif
|
||||
[AC_G722] = { .codec_id = AV_CODEC_ID_ADPCM_G722 },
|
||||
@@ -124,6 +124,8 @@ struct libavcodec_codec_state {
|
||||
|
||||
void *samples;
|
||||
int change_bps_to;
|
||||
|
||||
int bitrate;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -135,7 +137,8 @@ struct libavcodec_codec_state {
|
||||
* @retval NULL if initialization failed
|
||||
* @retval !=NULL codec state
|
||||
*/
|
||||
static void *libavcodec_init(audio_codec_t audio_codec, audio_codec_direction_t direction, bool try_init)
|
||||
static void *libavcodec_init(audio_codec_t audio_codec, audio_codec_direction_t direction, bool try_init,
|
||||
int bitrate)
|
||||
{
|
||||
int codec_id = 0;
|
||||
|
||||
@@ -176,6 +179,8 @@ static void *libavcodec_init(audio_codec_t audio_codec, audio_codec_direction_t
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s->bitrate = bitrate;
|
||||
|
||||
s->samples = NULL;
|
||||
|
||||
av_init_packet(&s->pkt);
|
||||
@@ -219,7 +224,7 @@ static bool reinitialize_coder(struct libavcodec_codec_state *s, struct audio_de
|
||||
pthread_mutex_unlock(s->libav_global_lock);
|
||||
|
||||
/* put sample parameters */
|
||||
s->codec_ctx->bit_rate = 64000;
|
||||
s->codec_ctx->bit_rate = s->bitrate;
|
||||
s->codec_ctx->sample_rate = desc.sample_rate;
|
||||
s->change_bps_to = 0;
|
||||
switch(desc.bps) {
|
||||
@@ -242,7 +247,7 @@ static bool reinitialize_coder(struct libavcodec_codec_state *s, struct audio_de
|
||||
}
|
||||
|
||||
s->codec_ctx->channels = 1;
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 55
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 54
|
||||
s->codec_ctx->channel_layout = AV_CH_LAYOUT_MONO;
|
||||
#endif
|
||||
|
||||
@@ -261,7 +266,7 @@ static bool reinitialize_coder(struct libavcodec_codec_state *s, struct audio_de
|
||||
|
||||
s->av_frame->nb_samples = s->codec_ctx->frame_size;
|
||||
s->av_frame->format = s->codec_ctx->sample_fmt;
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 55
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 54
|
||||
s->av_frame->channel_layout = AV_CH_LAYOUT_MONO;
|
||||
#endif
|
||||
|
||||
@@ -455,7 +460,7 @@ static void libavcodec_done(void *state)
|
||||
free(s->tmp.data);
|
||||
av_free_packet(&s->pkt);
|
||||
av_freep(&s->samples);
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 55
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 54
|
||||
avcodec_free_frame(&s->av_frame);
|
||||
#else
|
||||
av_free(s->av_frame);
|
||||
|
||||
@@ -161,7 +161,7 @@ int audio_play_alsa_reconfigure(void *state, int quant_samples, int channels,
|
||||
rc = snd_pcm_hw_params_set_rate_resample(s->handle,
|
||||
params, val);
|
||||
if(rc < 0) {
|
||||
fprintf(stderr, "[ALSA play.] Warnings: Unable to set resampling: %s\n",
|
||||
fprintf(stderr, "[ALSA play.] Warning: Unable to set resampling: %s\n",
|
||||
snd_strerror(rc));
|
||||
}
|
||||
|
||||
@@ -177,18 +177,19 @@ int audio_play_alsa_reconfigure(void *state, int quant_samples, int channels,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Set period size to 1 frame. */
|
||||
/* Set period to its minimal size.
|
||||
* Do not use snd_pcm_hw_params_set_period_size_near,
|
||||
* since it allows to set also unsupported value without notifying.
|
||||
* See also http://www.alsa-project.org/main/index.php/FramesPeriods */
|
||||
frames = 1;
|
||||
dir = 1;
|
||||
rc = snd_pcm_hw_params_set_period_size_near(s->handle,
|
||||
rc = snd_pcm_hw_params_set_period_time_min(s->handle,
|
||||
params, &frames, &dir);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "cannot set period time: %s\n",
|
||||
fprintf(stderr, "[ALSA play.] Warning: cannot set period time: %s\n",
|
||||
snd_strerror(rc));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
val = BUFFER_MIN * 1000;
|
||||
dir = 1;
|
||||
rc = snd_pcm_hw_params_set_buffer_time_min(s->handle, params,
|
||||
|
||||
@@ -423,28 +423,34 @@ void decklink_put_frame(void *state, struct audio_frame *frame)
|
||||
#else
|
||||
unsigned int sampleFramesWritten;
|
||||
#endif
|
||||
char *data = frame->data;
|
||||
// tmp_frame is used if we need to perform 1->2 channel multiplication
|
||||
struct audio_frame tmp_frame;
|
||||
tmp_frame.data = NULL;
|
||||
|
||||
/* we got probably count that cannot be played directly (probably 1) */
|
||||
/* we got probably channel count that cannot be played directly (probably 1) */
|
||||
if(s->output_audio_channel_count != s->audio_desc.ch_count) {
|
||||
assert(s->audio_desc.ch_count == 1); /* only reasonable value so far */
|
||||
if (sampleFrameCount * s->output_audio_channel_count
|
||||
* frame->bps > frame->max_size) {
|
||||
fprintf(stderr, "[decklink] audio buffer overflow!\n");
|
||||
sampleFrameCount = frame->max_size /
|
||||
(s->output_audio_channel_count * frame->bps);
|
||||
frame->data_len = sampleFrameCount *
|
||||
(frame->ch_count * frame->bps);
|
||||
}
|
||||
assert(s->audio_desc.ch_count == 1); /* only supported value so far */
|
||||
memcpy(&tmp_frame, frame, sizeof(tmp_frame));
|
||||
// allocate enough space to hold resulting data
|
||||
tmp_frame.max_size = sampleFrameCount * s->output_audio_channel_count
|
||||
* frame->bps;
|
||||
tmp_frame.data = (char *) malloc(tmp_frame.max_size);
|
||||
memcpy(tmp_frame.data, frame->data, frame->data_len);
|
||||
|
||||
audio_frame_multiply_channel(frame,
|
||||
audio_frame_multiply_channel(&tmp_frame,
|
||||
s->output_audio_channel_count);
|
||||
|
||||
data = tmp_frame.data;
|
||||
}
|
||||
|
||||
s->deckLinkOutput->ScheduleAudioSamples (frame->data, sampleFrameCount, 0,
|
||||
s->deckLinkOutput->ScheduleAudioSamples (data, sampleFrameCount, 0,
|
||||
0, &sampleFramesWritten);
|
||||
if(sampleFramesWritten != sampleFrameCount)
|
||||
fprintf(stderr, "[decklink] audio buffer underflow!\n");
|
||||
|
||||
free(tmp_frame.data);
|
||||
|
||||
}
|
||||
|
||||
int decklink_reconfigure(void *state, int quant_samples, int channels,
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
#define MAX_PORTS 64
|
||||
|
||||
struct state_jack_playback {
|
||||
const char *jack_ports_pattern;
|
||||
char *jack_ports_pattern;
|
||||
int jack_sample_rate;
|
||||
jack_client_t *client;
|
||||
jack_port_t *output_port[MAX_PORTS];
|
||||
@@ -78,6 +78,7 @@ struct state_jack_playback {
|
||||
float *converted;
|
||||
#ifdef HAVE_SPEEX
|
||||
float *converted_resampled;
|
||||
size_t converted_resampled_size;
|
||||
SpeexResamplerState *resampler;
|
||||
#endif
|
||||
|
||||
@@ -190,7 +191,7 @@ void * audio_play_jack_init(char *cfg)
|
||||
|
||||
s = calloc(1, sizeof(struct state_jack_playback));
|
||||
|
||||
s->jack_ports_pattern = cfg;
|
||||
s->jack_ports_pattern = strdup(cfg);
|
||||
|
||||
if(!s) {
|
||||
fprintf(stderr, "[JACK playback] Unable to allocate memory.\n");
|
||||
@@ -287,7 +288,8 @@ int audio_play_jack_reconfigure(void *state, int quant_samples, int channels,
|
||||
if(s->resampler) {
|
||||
speex_resampler_destroy(s->resampler);
|
||||
}
|
||||
s->converted_resampled = (float *) malloc(sizeof(float) * s->jack_sample_rate);
|
||||
s->converted_resampled_size = sizeof(float) * s->jack_sample_rate;
|
||||
s->converted_resampled = (float *) malloc(s->converted_resampled_size);
|
||||
|
||||
{
|
||||
int err;
|
||||
@@ -337,7 +339,7 @@ void audio_play_jack_put_frame(void *state, struct audio_frame *frame)
|
||||
int2float((char *) s->converted, (char *) s->converted, converted_size);
|
||||
#ifdef HAVE_SPEEX
|
||||
spx_uint32_t in_len = channel_size / frame->bps;
|
||||
spx_uint32_t out_len;
|
||||
spx_uint32_t out_len = s->converted_resampled_size;
|
||||
speex_resampler_process_float(s->resampler,
|
||||
i,
|
||||
s->converted,
|
||||
@@ -364,6 +366,7 @@ void audio_play_jack_done(void *state)
|
||||
#endif
|
||||
free(s->channel);
|
||||
free(s->converted);
|
||||
free(s->jack_ports_pattern);
|
||||
for(i = 0; i < MAX_PORTS; ++i) {
|
||||
ring_buffer_destroy(s->data[i]);
|
||||
}
|
||||
|
||||
@@ -80,15 +80,11 @@ void audio_frame2_allocate(audio_frame2 *frame, int nr_channels, int max_size)
|
||||
{
|
||||
assert(nr_channels <= MAX_AUDIO_CHANNELS);
|
||||
|
||||
audio_frame2_reset(frame);
|
||||
|
||||
frame->max_size = max_size;
|
||||
frame->ch_count = nr_channels;
|
||||
|
||||
for(int i = 0; i < MAX_AUDIO_CHANNELS; ++i) {
|
||||
free(frame->data[i]);
|
||||
frame->data[i] = NULL;
|
||||
frame->data_len[i] = 0;
|
||||
}
|
||||
|
||||
for(int i = 0; i < nr_channels; ++i) {
|
||||
frame->data[i] = malloc(max_size);
|
||||
}
|
||||
@@ -96,6 +92,7 @@ void audio_frame2_allocate(audio_frame2 *frame, int nr_channels, int max_size)
|
||||
|
||||
void audio_frame2_append(audio_frame2 *dest, audio_frame2 *src)
|
||||
{
|
||||
assert(src->ch_count == dest->ch_count || dest->ch_count == 0);
|
||||
dest->bps = src->bps;
|
||||
int new_max_size = dest->max_size;
|
||||
for (int i = 0; i < src->ch_count; ++i) {
|
||||
@@ -126,9 +123,13 @@ int audio_frame2_get_sample_count(audio_frame2 *frame)
|
||||
|
||||
void audio_frame2_reset(audio_frame2 *frame)
|
||||
{
|
||||
for (int i = 0; i < frame->ch_count; ++i) {
|
||||
for(int i = 0; i < MAX_AUDIO_CHANNELS; ++i) {
|
||||
frame->data_len[i] = 0;
|
||||
free(frame->data[i]);
|
||||
frame->data[i] = NULL;
|
||||
}
|
||||
frame->max_size = 0;
|
||||
frame->ch_count = 0;
|
||||
}
|
||||
|
||||
static double get_normalized(char *in, int bps) {
|
||||
@@ -251,19 +252,15 @@ static inline int32_t format_from_in_bps(const char * in, int bps) {
|
||||
}
|
||||
|
||||
static inline void format_to_out_bps(char *out, int bps, int32_t out_value) {
|
||||
uint32_t mask;
|
||||
if(bps == sizeof(uint32_t)) {
|
||||
mask = 0xffffffffu - 1;
|
||||
} else {
|
||||
mask = ((1 << (bps * 8)) - 1);
|
||||
uint32_t mask = ((1ll << (bps * 8)) - 1);
|
||||
|
||||
// clamp
|
||||
if(out_value > (1ll << (bps * 8 - 1)) -1) {
|
||||
out_value = (1ll << (bps * 8 - 1)) -1;
|
||||
}
|
||||
|
||||
if(out_value > (1 << (bps * 8 - 1)) -1) {
|
||||
out_value = (1 << (bps * 8 - 1)) -1;
|
||||
}
|
||||
|
||||
if(out_value < -(1 << (bps * 8 - 1))) {
|
||||
out_value = -(1 << (bps * 8 - 1));
|
||||
if(out_value < -(1ll << (bps * 8 - 1))) {
|
||||
out_value = -(1ll << (bps * 8 - 1));
|
||||
}
|
||||
|
||||
uint32_t out_value_formatted = (1 * (0x1 & (out_value >> 31))) << (bps * 8 - 1) | (out_value & mask);
|
||||
@@ -480,32 +477,3 @@ void audio_channel_mux(audio_frame2 *frame, int index, audio_channel *channel)
|
||||
frame->sample_rate = channel->sample_rate;
|
||||
}
|
||||
|
||||
audio_codec_t get_audio_codec_to_name(const char *codec) {
|
||||
for(int i = 0; i < audio_codec_info_len; ++i) {
|
||||
if(strcasecmp(audio_codec_info[i].name, codec) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return AC_NONE;
|
||||
}
|
||||
|
||||
const char *get_name_to_audio_codec(audio_codec_t codec)
|
||||
{
|
||||
return audio_codec_info[codec].name;
|
||||
}
|
||||
|
||||
uint32_t get_audio_tag(audio_codec_t codec)
|
||||
{
|
||||
return audio_codec_info[codec].tag;
|
||||
}
|
||||
|
||||
audio_codec_t get_audio_codec_to_tag(uint32_t tag)
|
||||
{
|
||||
for(int i = 0; i < audio_codec_info_len; ++i) {
|
||||
if(audio_codec_info[i].tag == tag) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return AC_NONE;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,11 +72,6 @@ struct audio_desc audio_desc_from_audio_channel(audio_channel *);
|
||||
void audio_channel_demux(audio_frame2 *, int, audio_channel*);
|
||||
void audio_channel_mux(audio_frame2 *, int, audio_channel*);
|
||||
|
||||
audio_codec_t get_audio_codec_to_name(const char *name);
|
||||
const char *get_name_to_audio_codec(audio_codec_t codec);
|
||||
uint32_t get_audio_tag(audio_codec_t codec);
|
||||
audio_codec_t get_audio_codec_to_tag(uint32_t audio_tag);
|
||||
|
||||
/**
|
||||
* Changes bps for everey sample.
|
||||
*
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
/**
|
||||
* @file capture_filter/logo.c
|
||||
* @author Martin Pulec <pulec@cesnet.cz>
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2013 CESNET z.s.p.o.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, is permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of CESNET nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#include "config_unix.h"
|
||||
#include "config_win32.h"
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include "src/cesnet-logo-2.c"
|
||||
#include "capture_filter.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "video.h"
|
||||
#include "video_codec.h"
|
||||
|
||||
struct state_capture_filter_logo {
|
||||
int x, y;
|
||||
};
|
||||
|
||||
static int init(struct module *parent, const char *cfg, void **state);
|
||||
static void done(void *state);
|
||||
static struct video_frame *filter(void *state, struct video_frame *in);
|
||||
|
||||
static int init(struct module *parent, const char *cfg, void **state)
|
||||
{
|
||||
UNUSED(parent);
|
||||
struct state_capture_filter_logo *s = (struct state_capture_filter_logo *)
|
||||
calloc(1, sizeof(struct state_capture_filter_logo));
|
||||
|
||||
s->x = s->y = -1;
|
||||
|
||||
if (cfg) {
|
||||
if (strcasecmp(cfg, "help") == 0) {
|
||||
printf("Draws overlay logo over video:\n\n");
|
||||
printf("'logo' usage:\n");
|
||||
printf("\tlogo[:<x>[:<y>]]\n\n");
|
||||
free(s);
|
||||
return 1;
|
||||
}
|
||||
char *tmp = strdup(cfg);
|
||||
char *save_ptr = NULL;
|
||||
char *item;
|
||||
if ((item = strtok_r(tmp, ":", &save_ptr))) {
|
||||
s->x = atoi(item);
|
||||
if ((item = strtok_r(NULL, ":", &save_ptr))) {
|
||||
s->y = atoi(item);
|
||||
}
|
||||
}
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
*state = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void done(void *state)
|
||||
{
|
||||
free(state);
|
||||
}
|
||||
|
||||
static struct video_frame *filter(void *state, struct video_frame *in)
|
||||
{
|
||||
struct state_capture_filter_logo *s = (struct state_capture_filter_logo *)
|
||||
state;
|
||||
const int width = cesnet_logo.width;
|
||||
const int height = cesnet_logo.height;
|
||||
int linesize = width * 3;
|
||||
decoder_t decoder, coder;
|
||||
decoder = get_decoder_from_to(in->color_spec, RGB, true);
|
||||
coder = get_decoder_from_to(RGB, in->color_spec, true);
|
||||
int rect_x = s->x;
|
||||
int rect_y = s->y;
|
||||
|
||||
if (decoder == NULL || coder == NULL)
|
||||
return in;
|
||||
|
||||
if (rect_x < 0 || rect_x + width > (int) in->tiles[0].width) {
|
||||
rect_x = in->tiles[0].width - width;
|
||||
}
|
||||
rect_x = (rect_x / get_pf_block_size(in->color_spec)) * get_pf_block_size(in->color_spec);
|
||||
|
||||
if (rect_y < 0 || rect_y + height > (int) in->tiles[0].height) {
|
||||
rect_y = in->tiles[0].height - height;
|
||||
}
|
||||
|
||||
if (rect_x < 0 || rect_y < 0)
|
||||
return in;
|
||||
|
||||
unsigned char *segment = (unsigned char *) malloc(width * height * 3);
|
||||
|
||||
for (int y = 0; y < height; ++y) {
|
||||
decoder(segment + y * linesize, (unsigned char *) in->tiles[0].data + (y + rect_y) *
|
||||
vc_get_linesize(in->tiles[0].width, in->color_spec) +
|
||||
vc_get_linesize(rect_x, in->color_spec), linesize,
|
||||
0, 8, 16);
|
||||
}
|
||||
|
||||
unsigned char *image_data = segment;
|
||||
const unsigned char *overlay_data = cesnet_logo.pixel_data;
|
||||
for (int y = 0; y < height; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
int alpha = overlay_data[3];
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
*image_data = (*image_data * (255 - alpha) + *overlay_data++ * alpha) / 255;
|
||||
image_data++;
|
||||
}
|
||||
overlay_data++; // skip alpha
|
||||
}
|
||||
}
|
||||
|
||||
for (int y = 0; y < height; ++y) {
|
||||
coder((unsigned char *) in->tiles[0].data + (rect_y + y) *
|
||||
vc_get_linesize(in->tiles[0].width, in->color_spec) +
|
||||
vc_get_linesize(rect_x, in->color_spec),
|
||||
segment + y * linesize,
|
||||
vc_get_linesize(width, in->color_spec), 0, 8, 16);
|
||||
}
|
||||
|
||||
free(segment);
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
struct capture_filter_info capture_filter_logo = {
|
||||
.name = "logo",
|
||||
.init = init,
|
||||
.done = done,
|
||||
.filter = filter,
|
||||
};
|
||||
|
||||
236
src/capture_filter/logo.cpp
Normal file
236
src/capture_filter/logo.cpp
Normal file
@@ -0,0 +1,236 @@
|
||||
/**
|
||||
* @file capture_filter/logo.c
|
||||
* @author Martin Pulec <pulec@cesnet.cz>
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2013 CESNET z.s.p.o.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, is permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of CESNET nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#include "config_unix.h"
|
||||
#include "config_win32.h"
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include "capture_filter.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include "video.h"
|
||||
#include "video_codec.h"
|
||||
|
||||
struct state_capture_filter_logo {
|
||||
unsigned char *logo;
|
||||
unsigned int width, height;
|
||||
int x, y;
|
||||
};
|
||||
|
||||
static int init(struct module *parent, const char *cfg, void **state);
|
||||
static void done(void *state);
|
||||
static struct video_frame *filter(void *state, struct video_frame *in);
|
||||
|
||||
using namespace std;
|
||||
|
||||
static bool load_logo_data_from_file(struct state_capture_filter_logo *s, const char *filename) {
|
||||
try {
|
||||
string line;
|
||||
ifstream file(filename, ifstream::in | ifstream::binary);
|
||||
|
||||
file.exceptions(ifstream::failbit | ifstream::badbit );
|
||||
|
||||
getline(file, line);
|
||||
if (!file.good() || line != "P7") {
|
||||
throw string("Only logo in PAM format is currently supported.");
|
||||
}
|
||||
getline(file, line);
|
||||
while (!file.eof()) {
|
||||
if (line.compare(0, strlen("WIDTH"), "WIDTH") == 0) {
|
||||
s->width = atoi(line.c_str() + strlen("WIDTH "));
|
||||
} else if (line.compare(0, strlen("HEIGHT"), "HEIGHT") == 0) {
|
||||
s->height = atoi(line.c_str() + strlen("HEIGHT "));
|
||||
} else if (line.compare(0, strlen("DEPTH"), "DEPTH") == 0) {
|
||||
if (atoi(line.c_str() + strlen("DEPTH ")) != 4) {
|
||||
throw string("Only depth 4 is supported.");
|
||||
}
|
||||
} else if (line.compare(0, strlen("MAXVAL"), "MAXVAL") == 0) {
|
||||
if (atoi(line.c_str() + strlen("MAXVAL ")) != 255) {
|
||||
throw string("Only supported maxval is 255.");
|
||||
}
|
||||
} else if (line.compare(0, strlen("TUPLETYPE"), "MAXVAL") == 0) {
|
||||
if (line.compare("TUPLTYPE RGB_ALPHA") != 0) {
|
||||
throw string("Only supported tuple type is RGBA_APLHA.");
|
||||
}
|
||||
} else if (line.compare(0, strlen("ENDHDR"), "ENDHDR") == 0) {
|
||||
break;
|
||||
}
|
||||
getline(file, line);
|
||||
}
|
||||
int datalen = 4 * s->width * s->height;
|
||||
s->logo = (unsigned char *) malloc(datalen);
|
||||
file.read((char *) s->logo, datalen);
|
||||
if (file.eof()) {
|
||||
throw string("Unable to load logo data from file.");
|
||||
}
|
||||
file.close();
|
||||
} catch (string const & s) {
|
||||
cerr << s << endl;
|
||||
return false;
|
||||
} catch (exception const & e) {
|
||||
cerr << e.what() << endl;
|
||||
return false;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int init(struct module *parent, const char *cfg, void **state)
|
||||
{
|
||||
UNUSED(parent);
|
||||
struct state_capture_filter_logo *s = (struct state_capture_filter_logo *)
|
||||
calloc(1, sizeof(struct state_capture_filter_logo));
|
||||
|
||||
s->x = s->y = -1;
|
||||
|
||||
if (!cfg || strcasecmp(cfg, "help") == 0) {
|
||||
printf("Draws overlay logo over video:\n\n");
|
||||
printf("'logo' usage:\n");
|
||||
printf("\tlogo:<file>[:<x>[:<y>]]\n");
|
||||
printf("\t\t<file> - is path to logo to be added in PAM format with alpha\n");
|
||||
free(s);
|
||||
return 1;
|
||||
}
|
||||
char *tmp = strdup(cfg);
|
||||
char *save_ptr = NULL;
|
||||
char *item;
|
||||
if ((item = strtok_r(tmp, ":", &save_ptr)) == NULL) {
|
||||
fprintf(stderr, "File name with logo required!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!load_logo_data_from_file(s, item)) {
|
||||
free(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((item = strtok_r(tmp, ":", &save_ptr))) {
|
||||
s->x = atoi(item);
|
||||
if ((item = strtok_r(NULL, ":", &save_ptr))) {
|
||||
s->y = atoi(item);
|
||||
}
|
||||
}
|
||||
free(tmp);
|
||||
|
||||
*state = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void done(void *state)
|
||||
{
|
||||
struct state_capture_filter_logo *s = (struct state_capture_filter_logo *)
|
||||
state;
|
||||
free(s->logo);
|
||||
free(state);
|
||||
}
|
||||
|
||||
static struct video_frame *filter(void *state, struct video_frame *in)
|
||||
{
|
||||
struct state_capture_filter_logo *s = (struct state_capture_filter_logo *)
|
||||
state;
|
||||
int linesize = s->width * 3;
|
||||
decoder_t decoder, coder;
|
||||
decoder = get_decoder_from_to(in->color_spec, RGB, true);
|
||||
coder = get_decoder_from_to(RGB, in->color_spec, true);
|
||||
int rect_x = s->x;
|
||||
int rect_y = s->y;
|
||||
|
||||
if (decoder == NULL || coder == NULL)
|
||||
return in;
|
||||
|
||||
if (rect_x < 0 || rect_x + s->width > in->tiles[0].width) {
|
||||
rect_x = in->tiles[0].width - s->width;
|
||||
}
|
||||
rect_x = (rect_x / get_pf_block_size(in->color_spec)) * get_pf_block_size(in->color_spec);
|
||||
|
||||
if (rect_y < 0 || rect_y + s->height > in->tiles[0].height) {
|
||||
rect_y = in->tiles[0].height - s->height;
|
||||
}
|
||||
|
||||
if (rect_x < 0 || rect_y < 0)
|
||||
return in;
|
||||
|
||||
unsigned char *segment = (unsigned char *) malloc(s->width * s->height * 3);
|
||||
|
||||
for (unsigned int y = 0; y < s->height; ++y) {
|
||||
decoder(segment + y * linesize, (unsigned char *) in->tiles[0].data + (y + rect_y) *
|
||||
vc_get_linesize(in->tiles[0].width, in->color_spec) +
|
||||
vc_get_linesize(rect_x, in->color_spec), linesize,
|
||||
0, 8, 16);
|
||||
}
|
||||
|
||||
unsigned char *image_data = segment;
|
||||
const unsigned char *overlay_data = s->logo;
|
||||
for (unsigned int y = 0; y < s->height; ++y) {
|
||||
for (unsigned int x = 0; x < s->width; ++x) {
|
||||
int alpha = overlay_data[3];
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
*image_data = (*image_data * (255 - alpha) + *overlay_data++ * alpha) / 255;
|
||||
image_data++;
|
||||
}
|
||||
overlay_data++; // skip alpha
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int y = 0; y < s->height; ++y) {
|
||||
coder((unsigned char *) in->tiles[0].data + (rect_y + y) *
|
||||
vc_get_linesize(in->tiles[0].width, in->color_spec) +
|
||||
vc_get_linesize(rect_x, in->color_spec),
|
||||
segment + y * linesize,
|
||||
vc_get_linesize(s->width, in->color_spec), 0, 8, 16);
|
||||
}
|
||||
|
||||
free(segment);
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
struct capture_filter_info capture_filter_logo = {
|
||||
"logo",
|
||||
init,
|
||||
done,
|
||||
filter,
|
||||
};
|
||||
|
||||
7332
src/cesnet-logo-2.c
7332
src/cesnet-logo-2.c
File diff suppressed because it is too large
Load Diff
@@ -300,10 +300,22 @@ static int process_msg(struct control_state *s, fd_t client_fd, char *message)
|
||||
abort();
|
||||
}
|
||||
|
||||
struct msg_sender *msg_audio = (struct msg_sender *) malloc(sizeof(struct msg_sender));
|
||||
memcpy(msg_audio, msg, sizeof(struct msg_sender));
|
||||
if (msg_audio->type == SENDER_MSG_CHANGE_PORT) {
|
||||
msg->port = atoi(suffix(message, "sender-port "));
|
||||
}
|
||||
|
||||
enum module_class path_sender[] = { MODULE_CLASS_SENDER, MODULE_CLASS_NONE };
|
||||
enum module_class path_sender_audio[] = { MODULE_CLASS_AUDIO, MODULE_CLASS_SENDER, MODULE_CLASS_NONE };
|
||||
char path_audio[1024];
|
||||
memcpy(path_audio, path, sizeof(path_audio));
|
||||
append_message_path(path, sizeof(path), path_sender);
|
||||
append_message_path(path_audio, sizeof(path_audio), path_sender_audio);
|
||||
|
||||
resp =
|
||||
send_message(s->root_module, path, (struct message *) msg);
|
||||
send_message(s->root_module, path_audio, (struct message *) msg_audio);
|
||||
} else if (prefix_matches(message, "receiver-port ")) {
|
||||
struct msg_receiver *msg =
|
||||
(struct msg_receiver *)
|
||||
@@ -349,7 +361,7 @@ static int process_msg(struct control_state *s, fd_t client_fd, char *message)
|
||||
char *compress = suffix(message, "compress ");
|
||||
|
||||
if(prefix_matches(compress, "param ")) {
|
||||
compress = suffix(compress, " param");
|
||||
compress = suffix(compress, "param ");
|
||||
msg->what = CHANGE_PARAMS;
|
||||
} else {
|
||||
msg->what = CHANGE_COMPRESS;
|
||||
@@ -357,7 +369,7 @@ static int process_msg(struct control_state *s, fd_t client_fd, char *message)
|
||||
strncpy(msg->config_string, compress, sizeof(msg->config_string) - 1);
|
||||
|
||||
if(!resp) {
|
||||
enum module_class path_compress[] = { MODULE_CLASS_COMPRESS, MODULE_CLASS_NONE };
|
||||
enum module_class path_compress[] = { MODULE_CLASS_SENDER, MODULE_CLASS_COMPRESS, MODULE_CLASS_NONE };
|
||||
append_message_path(path, sizeof(path), path_compress);
|
||||
resp = send_message(s->root_module, path, (struct message *) msg);
|
||||
}
|
||||
|
||||
@@ -41,6 +41,8 @@ bool verbose = false;
|
||||
|
||||
bool ldgm_device_gpu = false;
|
||||
|
||||
const char *window_title = NULL;
|
||||
|
||||
int rxtx_mode; // MODE_SENDER, MODE_RECEIVER or both
|
||||
|
||||
int initialize_video_capture(struct module *parent,
|
||||
|
||||
@@ -96,6 +96,8 @@ extern bool verbose;
|
||||
|
||||
extern bool ldgm_device_gpu;
|
||||
|
||||
extern const char *window_title;
|
||||
|
||||
#define MODE_SENDER 1
|
||||
#define MODE_RECEIVER 2
|
||||
extern int rxtx_mode;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <libavutil/opt.h>
|
||||
#include <libavutil/pixfmt.h>
|
||||
|
||||
#if !defined(HAVE_AVCODEC_ENCODE_VIDEO2) || (LIBAVCODEC_VERSION_MAJOR < 55)
|
||||
#if LIBAVCODEC_VERSION_MAJOR < 54
|
||||
#define AV_PIX_FMT_NONE PIX_FMT_NONE
|
||||
#define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P
|
||||
#define AV_PIX_FMT_YUV422P PIX_FMT_YUV422P
|
||||
|
||||
78
src/main.cpp
78
src/main.cpp
@@ -127,6 +127,7 @@
|
||||
#define OPT_CONTROL_PORT (('C' << 8) | 'P')
|
||||
#define OPT_VERBOSE (('V' << 8) | 'E')
|
||||
#define OPT_LDGM_DEVICE (('L' << 8) | 'D')
|
||||
#define OPT_WINDOW_TITLE (('W' << 8) | 'T')
|
||||
|
||||
#define MAX_CAPTURE_COUNT 17
|
||||
|
||||
@@ -433,7 +434,6 @@ int main(int argc, char *argv[])
|
||||
const char *requested_audio_fec = DEFAULT_AUDIO_FEC;
|
||||
char *audio_channel_map = NULL;
|
||||
const char *audio_scale = "mixauto";
|
||||
rtsp_serv_t* rtsp_server = NULL;
|
||||
bool isStd = FALSE;
|
||||
int recv_port_number = PORT_BASE;
|
||||
int send_port_number = PORT_BASE;
|
||||
@@ -457,14 +457,13 @@ int main(int argc, char *argv[])
|
||||
struct state_uv *uv;
|
||||
int ch;
|
||||
|
||||
audio_codec_t audio_codec = AC_PCM;
|
||||
const char *audio_codec = "PCM";
|
||||
|
||||
pthread_t receiver_thread_id,
|
||||
capture_thread_id;
|
||||
bool receiver_thread_started = false,
|
||||
capture_thread_started = false;
|
||||
bool receiver_thread_started = false,
|
||||
capture_thread_started = false;
|
||||
unsigned display_flags = 0;
|
||||
int compressed_audio_sample_rate = 48000;
|
||||
int ret;
|
||||
struct vidcap_params *audio_cap_dev;
|
||||
long packet_rate;
|
||||
@@ -524,6 +523,7 @@ int main(int argc, char *argv[])
|
||||
{"encryption", required_argument, 0, OPT_ENCRYPTION},
|
||||
{"verbose", no_argument, 0, OPT_VERBOSE},
|
||||
{"ldgm-device", required_argument, 0, OPT_LDGM_DEVICE},
|
||||
{"window-title", required_argument, 0, OPT_WINDOW_TITLE},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
int option_index = 0;
|
||||
@@ -553,11 +553,11 @@ int main(int argc, char *argv[])
|
||||
return 0;
|
||||
}
|
||||
requested_display = optarg;
|
||||
if(strchr(optarg, ':')) {
|
||||
char *delim = strchr(optarg, ':');
|
||||
*delim = '\0';
|
||||
display_cfg = delim + 1;
|
||||
}
|
||||
if(strchr(optarg, ':')) {
|
||||
char *delim = strchr(optarg, ':');
|
||||
*delim = '\0';
|
||||
display_cfg = delim + 1;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (!strcmp(optarg, "help")) {
|
||||
@@ -635,9 +635,9 @@ int main(int argc, char *argv[])
|
||||
requested_video_fec = optarg;
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
usage();
|
||||
return 0;
|
||||
case 'h':
|
||||
usage();
|
||||
return 0;
|
||||
case 'P':
|
||||
if(strchr(optarg, ':')) {
|
||||
char *save_ptr = NULL;
|
||||
@@ -741,12 +741,8 @@ int main(int argc, char *argv[])
|
||||
list_audio_codecs();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
if(strchr(optarg, ':')) {
|
||||
compressed_audio_sample_rate = atoi(strchr(optarg, ':')+1);
|
||||
*strchr(optarg, ':') = '\0';
|
||||
}
|
||||
audio_codec = get_audio_codec_to_name(optarg);
|
||||
if(audio_codec == AC_NONE) {
|
||||
audio_codec = optarg;
|
||||
if(get_audio_codec(optarg) == AC_NONE) {
|
||||
fprintf(stderr, "Unknown audio codec entered: \"%s\"\n",
|
||||
optarg);
|
||||
return EXIT_FAIL_USAGE;
|
||||
@@ -771,6 +767,9 @@ int main(int argc, char *argv[])
|
||||
ldgm_device_gpu = false;
|
||||
}
|
||||
break;
|
||||
case OPT_WINDOW_TITLE:
|
||||
window_title = optarg;
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
usage();
|
||||
@@ -797,7 +796,7 @@ int main(int argc, char *argv[])
|
||||
printf("Audio playback : %s\n", audio_recv);
|
||||
printf("MTU : %d B\n", requested_mtu);
|
||||
printf("Video compression: %s\n", requested_compression);
|
||||
printf("Audio codec : %s\n", get_name_to_audio_codec(audio_codec));
|
||||
printf("Audio codec : %s\n", get_name_to_audio_codec(get_audio_codec(audio_codec)));
|
||||
printf("Network protocol : %s\n", video_rxtx::get_name(video_protocol));
|
||||
printf("Audio FEC : %s\n", requested_audio_fec);
|
||||
printf("Video FEC : %s\n", requested_video_fec);
|
||||
@@ -831,17 +830,17 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
WSADATA wsaData;
|
||||
int err = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if(err != 0) {
|
||||
fprintf(stderr, "WSAStartup failed with error %d.", err);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
|
||||
fprintf(stderr, "Counld not found usable version of Winsock.\n");
|
||||
WSACleanup();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
WSADATA wsaData;
|
||||
int err = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if(err != 0) {
|
||||
fprintf(stderr, "WSAStartup failed with error %d.", err);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
|
||||
fprintf(stderr, "Counld not found usable version of Winsock.\n");
|
||||
WSACleanup();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(control_init(control_port, &control, &root_mod) != 0) {
|
||||
@@ -866,7 +865,7 @@ int main(int argc, char *argv[])
|
||||
jack_cfg, requested_audio_fec, requested_encryption,
|
||||
audio_channel_map,
|
||||
audio_scale, echo_cancellation, ipv6, requested_mcast_if,
|
||||
audio_codec, compressed_audio_sample_rate, isStd, packet_rate);
|
||||
audio_codec, isStd, packet_rate);
|
||||
if(!uv->audio)
|
||||
goto cleanup;
|
||||
|
||||
@@ -957,11 +956,14 @@ int main(int argc, char *argv[])
|
||||
display_device, requested_mtu,
|
||||
argc, argv);
|
||||
}else if (video_protocol == H264_STD) {
|
||||
rtps_types_t avType;
|
||||
if(strcmp("none", vidcap_params_get_driver(vidcap_params_head)) != 0 && (strcmp("none",audio_send) != 0)) avType = avStdDyn; //AVStream
|
||||
else if((strcmp("none",audio_send) != 0)) avType = audioPCMUdyn; //AStream
|
||||
rtps_types_t avType;
|
||||
if(strcmp("none", vidcap_params_get_driver(vidcap_params_head)) != 0 && (strcmp("none",audio_send) != 0)) avType = avStdDyn; //AVStream
|
||||
else if((strcmp("none",audio_send) != 0)) avType = audioPCMUdyn; //AStream
|
||||
else if(strcmp("none", vidcap_params_get_driver(vidcap_params_head))) avType = videoH264; //VStream
|
||||
else printf("[RTSP SERVER CHECK] no stream type... check capture devices input...\n");
|
||||
else {
|
||||
printf("[RTSP SERVER CHECK] no stream type... check capture devices input...\n");
|
||||
return EXIT_FAIL_USAGE;
|
||||
}
|
||||
|
||||
uv->state_video_rxtx = new h264_rtp_video_rxtx(&root_mod, video_exporter,
|
||||
requested_compression, requested_encryption,
|
||||
@@ -1063,10 +1065,6 @@ cleanup:
|
||||
vidcap_params_head = next;
|
||||
}
|
||||
|
||||
#ifdef HAVE_RTSP_SERVER
|
||||
if(rtsp_server) c_stop_server(rtsp_server);
|
||||
#endif
|
||||
|
||||
module_done(&root_mod);
|
||||
free(uv);
|
||||
|
||||
|
||||
@@ -107,17 +107,17 @@ struct state_audio_decoder {
|
||||
unsigned int channel_remapping:1;
|
||||
struct channel_map channel_map;
|
||||
|
||||
struct scale_data *scale;
|
||||
struct scale_data *scale; ///< contains scaling metadata if we want to perform audio scaling
|
||||
bool fixed_scale;
|
||||
|
||||
audio_frame2 *received_frame;
|
||||
audio_frame2 *received_frame; ///< auxiliary buffer that holds undecoded audio frame data from network
|
||||
struct audio_codec_state *audio_decompress;
|
||||
struct resampler *resampler;
|
||||
|
||||
struct audio_desc saved_desc;
|
||||
uint32_t saved_audio_tag;
|
||||
|
||||
audio_frame2 *decoded; // for statistics
|
||||
audio_frame2 *decoded; ///< buffer that keeps audio samples from last 5 seconds (for statistics)
|
||||
|
||||
struct openssl_decrypt *decrypt;
|
||||
};
|
||||
@@ -434,6 +434,7 @@ int decode_audio_frame(struct coded_data *cdata, void *data)
|
||||
sample_rate, input_channels, input_channels == 1 ? "": "s", bps * 8,
|
||||
get_name_to_audio_codec(get_audio_codec_to_tag(audio_tag)));
|
||||
|
||||
audio_frame2_reset(decoder->decoded);
|
||||
|
||||
s->buffer.bps = device_bps;
|
||||
s->buffer.ch_count = output_channels;
|
||||
@@ -503,21 +504,21 @@ int decode_audio_frame(struct coded_data *cdata, void *data)
|
||||
|
||||
audio_frame2 *resampled = resampler_resample(decoder->resampler, decompressed);
|
||||
|
||||
s->buffer.data_len = resampled->data_len[0] * output_channels;
|
||||
if((int) s->buffer.max_size < s->buffer.data_len) {
|
||||
free(s->buffer.data);
|
||||
s->buffer.max_size = s->buffer.data_len;
|
||||
s->buffer.data = (char *) malloc(s->buffer.max_size);
|
||||
size_t new_data_len = s->buffer.data_len + resampled->data_len[0] * output_channels;
|
||||
if((int) s->buffer.max_size < new_data_len) {
|
||||
s->buffer.max_size = new_data_len;
|
||||
s->buffer.data = (char *) realloc(s->buffer.data, new_data_len);
|
||||
}
|
||||
|
||||
memset(s->buffer.data, 0, s->buffer.data_len);
|
||||
memset(s->buffer.data + s->buffer.data_len, 0, new_data_len - s->buffer.data_len);
|
||||
|
||||
// there is a mapping for channel
|
||||
for(int channel = 0; channel < resampled->ch_count; ++channel) {
|
||||
if(decoder->channel_remapping) {
|
||||
if(channel < decoder->channel_map.size) {
|
||||
for(int i = 0; i < decoder->channel_map.sizes[channel]; ++i) {
|
||||
mux_and_mix_channel(s->buffer.data, resampled->data[channel],
|
||||
mux_and_mix_channel(s->buffer.data + s->buffer.data_len,
|
||||
resampled->data[channel],
|
||||
resampled->bps, resampled->data_len[channel],
|
||||
output_channels, decoder->channel_map.map[channel][i],
|
||||
decoder->scale[decoder->fixed_scale ? 0 :
|
||||
@@ -525,11 +526,13 @@ int decode_audio_frame(struct coded_data *cdata, void *data)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mux_and_mix_channel(s->buffer.data, resampled->data[channel], resampled->bps,
|
||||
mux_and_mix_channel(s->buffer.data + s->buffer.data_len, resampled->data[channel],
|
||||
resampled->bps,
|
||||
resampled->data_len[channel], output_channels, channel,
|
||||
decoder->scale[decoder->fixed_scale ? 0 : input_channels].scale);
|
||||
}
|
||||
}
|
||||
s->buffer.data_len = new_data_len;
|
||||
|
||||
audio_frame2_append(decoder->decoded, resampled);
|
||||
|
||||
|
||||
@@ -349,7 +349,9 @@ static socket_udp *udp_init4(const char *addr, const char *iface,
|
||||
sizeof(udpbufsize)) != 0) {
|
||||
debug_msg("WARNING: Unable to increase UDP recvbuffer\n");
|
||||
}
|
||||
#if 0
|
||||
// please note that we currently need these 2 options on MSW if we want
|
||||
// to re-bind socket on runtime, otherwise bind() would return an error if
|
||||
// binding to same port that was previously bound and closed
|
||||
#ifdef SO_REUSEPORT
|
||||
if (SETSOCKOPT
|
||||
(s->fd, SOL_SOCKET, SO_REUSEPORT, (int *)&reuse,
|
||||
@@ -364,7 +366,6 @@ static socket_udp *udp_init4(const char *addr, const char *iface,
|
||||
socket_error("setsockopt SO_REUSEADDR");
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
s_in.sin_family = AF_INET;
|
||||
s_in.sin_addr.s_addr = INADDR_ANY;
|
||||
s_in.sin_port = htons(rx_port);
|
||||
|
||||
202
src/rtp/pbuf.c
202
src/rtp/pbuf.c
@@ -174,60 +174,60 @@ struct pbuf *pbuf_init(void)
|
||||
|
||||
static void add_coded_unit(struct pbuf_node *node, rtp_packet * pkt)
|
||||
{
|
||||
/* Add "pkt" to the frame represented by "node". The "node" has */
|
||||
/* previously been created, and has some coded data already... */
|
||||
/* Add "pkt" to the frame represented by "node". The "node" has */
|
||||
/* previously been created, and has some coded data already... */
|
||||
|
||||
/* New arrivals are added at the head of the list, which is stored */
|
||||
/* in descending order of packets as they arrive (NOT necessarily */
|
||||
/* descending sequence number order, as the network might reorder) */
|
||||
/* New arrivals are added at the head of the list, which is stored */
|
||||
/* in descending order of packets as they arrive (NOT necessarily */
|
||||
/* descending sequence number order, as the network might reorder) */
|
||||
|
||||
struct coded_data *tmp, *curr, *prv;
|
||||
struct coded_data *tmp, *curr, *prv;
|
||||
|
||||
assert(node->rtp_timestamp == pkt->ts);
|
||||
assert(node->cdata != NULL);
|
||||
assert(node->rtp_timestamp == pkt->ts);
|
||||
assert(node->cdata != NULL);
|
||||
|
||||
tmp = malloc(sizeof(struct coded_data));
|
||||
if (tmp == NULL) {
|
||||
/* this is bad, out of memory, drop the packet... */
|
||||
free(pkt);
|
||||
return;
|
||||
}
|
||||
|
||||
tmp->seqno = pkt->seq;
|
||||
tmp->data = pkt;
|
||||
node->mbit |= pkt->m;
|
||||
if((int16_t)(tmp->seqno - node->cdata->seqno) > 0){
|
||||
tmp->prv = NULL;
|
||||
tmp->nxt = node->cdata;
|
||||
node->cdata->prv = tmp;
|
||||
node->cdata = tmp;
|
||||
} else {
|
||||
curr = node->cdata;
|
||||
if (curr == NULL){
|
||||
/* this is bad, out of memory, drop the packet... */
|
||||
free(pkt);
|
||||
free_cdata(tmp);
|
||||
} else {
|
||||
while (curr != NULL && ((int16_t)(tmp->seqno - curr->seqno) < 0)){
|
||||
prv = curr;
|
||||
curr = curr->nxt;
|
||||
}
|
||||
if (curr == NULL) {
|
||||
tmp->nxt = NULL;
|
||||
tmp->prv = prv;
|
||||
prv->nxt = tmp;
|
||||
}else if ((int16_t)(tmp->seqno - curr->seqno) > 0){
|
||||
tmp->nxt = curr;
|
||||
tmp->prv = curr->prv;
|
||||
tmp->prv->nxt = tmp;
|
||||
curr->prv = tmp;
|
||||
} else {
|
||||
/* this is bad, something went terribly wrong... */
|
||||
tmp = malloc(sizeof(struct coded_data));
|
||||
if (tmp == NULL) {
|
||||
/* this is bad, out of memory, drop the packet... */
|
||||
free(pkt);
|
||||
free_cdata(tmp);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
tmp->seqno = pkt->seq;
|
||||
tmp->data = pkt;
|
||||
node->mbit |= pkt->m;
|
||||
if((int16_t)(tmp->seqno - node->cdata->seqno) > 0){
|
||||
tmp->prv = NULL;
|
||||
tmp->nxt = node->cdata;
|
||||
node->cdata->prv = tmp;
|
||||
node->cdata = tmp;
|
||||
} else {
|
||||
curr = node->cdata;
|
||||
if (curr == NULL){
|
||||
/* this is bad, out of memory, drop the packet... */
|
||||
free(pkt);
|
||||
free_cdata(tmp);
|
||||
} else {
|
||||
while (curr != NULL && ((int16_t)(tmp->seqno - curr->seqno) < 0)){
|
||||
prv = curr;
|
||||
curr = curr->nxt;
|
||||
}
|
||||
if (curr == NULL) {
|
||||
tmp->nxt = NULL;
|
||||
tmp->prv = prv;
|
||||
prv->nxt = tmp;
|
||||
}else if ((int16_t)(tmp->seqno - curr->seqno) > 0){
|
||||
tmp->nxt = curr;
|
||||
tmp->prv = curr->prv;
|
||||
tmp->prv->nxt = tmp;
|
||||
curr->prv = tmp;
|
||||
} else {
|
||||
/* this is bad, something went terribly wrong... */
|
||||
free(pkt);
|
||||
free_cdata(tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct pbuf_node *create_new_pnode(rtp_packet * pkt, double playout_delay)
|
||||
@@ -269,7 +269,6 @@ static struct pbuf_node *create_new_pnode(rtp_packet * pkt, double playout_delay
|
||||
void pbuf_insert(struct pbuf *playout_buf, rtp_packet * pkt)
|
||||
{
|
||||
struct pbuf_node *tmp;
|
||||
struct pbuf_node *curr;
|
||||
|
||||
pbuf_validate(playout_buf);
|
||||
|
||||
@@ -315,43 +314,47 @@ void pbuf_insert(struct pbuf *playout_buf, rtp_packet * pkt)
|
||||
return;
|
||||
}
|
||||
|
||||
if (playout_buf->last->rtp_timestamp < pkt->ts) {
|
||||
/* Packet belongs to a new frame... */
|
||||
tmp = create_new_pnode(pkt, playout_buf->playout_delay);
|
||||
playout_buf->last->nxt = tmp;
|
||||
tmp->prv = playout_buf->last;
|
||||
playout_buf->last = tmp;
|
||||
if (playout_buf->last->rtp_timestamp == pkt->ts) {
|
||||
/* Packet belongs to last frame in playout_buf this is the */
|
||||
/* most likely scenario - although... */
|
||||
add_coded_unit(playout_buf->last, pkt);
|
||||
} else {
|
||||
/* Packet belongs to a previous frame... */
|
||||
curr = playout_buf->last;
|
||||
while(curr != playout_buf->frst && curr->rtp_timestamp > pkt->ts){
|
||||
curr = curr->prv;
|
||||
}
|
||||
|
||||
if (curr->rtp_timestamp == pkt->ts) {
|
||||
/* Packet belongs to a previous existing frame... */
|
||||
add_coded_unit(curr, pkt);
|
||||
} else if (curr->rtp_timestamp < pkt->ts){
|
||||
/* Packet belongs to a new previous frame */
|
||||
tmp = create_new_pnode(pkt, playout_buf->playout_delay);
|
||||
tmp->nxt = curr->nxt;
|
||||
tmp->prv = curr;
|
||||
curr->nxt->prv = tmp;
|
||||
curr->nxt = tmp;
|
||||
} else if (curr == playout_buf->frst) {
|
||||
tmp = create_new_pnode(pkt, playout_buf->playout_delay);
|
||||
tmp->nxt = playout_buf->frst;
|
||||
curr->prv = tmp;
|
||||
playout_buf->frst = tmp;
|
||||
|
||||
} else {
|
||||
|
||||
if (pkt->m) {
|
||||
debug_msg
|
||||
("Oops... dropped packet with M bit set\n");
|
||||
if (playout_buf->last->rtp_timestamp < pkt->ts) {
|
||||
/* Packet belongs to a new frame... */
|
||||
tmp = create_new_pnode(pkt, playout_buf->playout_delay);
|
||||
playout_buf->last->nxt = tmp;
|
||||
playout_buf->last->completed = true;
|
||||
tmp->prv = playout_buf->last;
|
||||
playout_buf->last = tmp;
|
||||
} else {
|
||||
bool discard_pkt = false;
|
||||
/* Packet belongs to a previous frame... */
|
||||
if (playout_buf->frst->rtp_timestamp > pkt->ts) {
|
||||
debug_msg("A very old packet - discarded\n");
|
||||
discard_pkt = true;
|
||||
} else {
|
||||
debug_msg
|
||||
("A packet for a previous frame, but might still be useful\n");
|
||||
struct pbuf_node *curr = playout_buf->last;
|
||||
while(curr != playout_buf->frst && curr->rtp_timestamp > pkt->ts){
|
||||
curr = curr->prv;
|
||||
}
|
||||
if (curr->rtp_timestamp == pkt->ts) {
|
||||
/* Packet belongs to a previous existing frame... */
|
||||
add_coded_unit(curr, pkt);
|
||||
} else {
|
||||
/* Packet belongs to a frame that is not present */
|
||||
discard_pkt = true;
|
||||
}
|
||||
}
|
||||
if (pkt->m && discard_pkt) {
|
||||
debug_msg
|
||||
("Oops... dropped packet with M bit set\n");
|
||||
}
|
||||
if (discard_pkt) {
|
||||
free(pkt);
|
||||
}
|
||||
}
|
||||
free(pkt);
|
||||
}
|
||||
}
|
||||
pbuf_validate(playout_buf);
|
||||
}
|
||||
@@ -462,37 +465,6 @@ pbuf_decode(struct pbuf *playout_buf, struct timeval curr_time,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
audio_pbuf_decode(struct pbuf *playout_buf, struct timeval curr_time,
|
||||
decode_frame_t decode_func, void *data)
|
||||
{
|
||||
/* Find the first complete frame that has reached it's playout */
|
||||
/* time, and decode it into the framebuffer. Mark the frame as */
|
||||
/* decoded, but otherwise leave it in the playout buffer. */
|
||||
struct pbuf_node *curr;
|
||||
|
||||
pbuf_validate(playout_buf);
|
||||
|
||||
curr = playout_buf->frst;
|
||||
while (curr != NULL) {
|
||||
/* WARNING: this one differs from video - we need to push audio immediately, because we do
|
||||
* _not_ know the granularity of audio (typically 256 B for ALSA) which is only small fractal
|
||||
* of frame time. The current RTP library isn't currently able to keep concurrently more frames.
|
||||
*/
|
||||
UNUSED(curr_time);
|
||||
if (!curr->decoded // && tv_gt(curr_time, curr->playout_time)
|
||||
) {
|
||||
if (curr->mbit == 1) {
|
||||
int ret = decode_func(curr->cdata, data);
|
||||
curr->decoded = 1;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
curr = curr->nxt;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pbuf_set_playout_delay(struct pbuf *playout_buf, double playout_delay)
|
||||
{
|
||||
playout_buf->playout_delay = playout_delay;
|
||||
|
||||
@@ -110,8 +110,6 @@ typedef int decode_frame_t(struct coded_data *cdata, void *decode_data);
|
||||
*/
|
||||
struct pbuf *pbuf_init(void);
|
||||
void pbuf_insert(struct pbuf *playout_buf, rtp_packet *r);
|
||||
int audio_pbuf_decode(struct pbuf *playout_buf, struct timeval curr_time,
|
||||
decode_frame_t decode_func, void *data);
|
||||
int pbuf_is_empty(struct pbuf *playout_buf);
|
||||
int pbuf_decode(struct pbuf *playout_buf, struct timeval curr_time,
|
||||
decode_frame_t decode_func, void *data);
|
||||
|
||||
156
src/switcher_control_keyboard.c
Normal file
156
src/switcher_control_keyboard.c
Normal file
@@ -0,0 +1,156 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#include "config_unix.h"
|
||||
#include "config_win32.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
|
||||
#ifdef HAVE_NCURSES
|
||||
|
||||
#include <curses.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "debug.h"
|
||||
|
||||
void usage(const char *progname);
|
||||
int main(int argc, char *argv[]);
|
||||
void sig_handler(int signal);
|
||||
|
||||
void usage(const char *progname) {
|
||||
printf("Usage: %s <hostname> <port>\n", progname);
|
||||
}
|
||||
|
||||
int fd = -1;// = socket(AF_INET6, SOCK_STREAM, 0);
|
||||
|
||||
void sig_handler(int signal) {
|
||||
UNUSED(signal);
|
||||
close(fd);
|
||||
endwin();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static void *reading_thread(void *arg) {
|
||||
UNUSED(arg);
|
||||
ssize_t bytes;
|
||||
char buf[1024];
|
||||
while ((bytes = recv(fd, buf, sizeof(buf), 0)) > 0) {
|
||||
write(1, buf, bytes);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
pthread_t reading_thread_id;
|
||||
|
||||
if(argc != 3) {
|
||||
usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
signal(SIGPIPE, sig_handler);
|
||||
|
||||
const char *hostname = argv[1];
|
||||
uint16_t port = atoi(argv[2]);
|
||||
|
||||
struct addrinfo hints, *res, *res0;
|
||||
int err;
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
const char *port_str = argv[2];
|
||||
err = getaddrinfo(hostname, port_str, &hints, &res0);
|
||||
|
||||
if(err) {
|
||||
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
char what[1024];
|
||||
|
||||
for (res = res0; res; res = res->ai_next) {
|
||||
fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||
if (fd < 0) {
|
||||
snprintf(what, 1024, "socket failed: %s", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
if(connect(fd, res->ai_addr, res->ai_addrlen) == -1) {
|
||||
fd = -1;
|
||||
|
||||
snprintf(what, 1024, "connect failed: %s:%d :%s", hostname, port, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
break; /* okay we got one */
|
||||
}
|
||||
|
||||
freeaddrinfo(res0);
|
||||
|
||||
if(fd < 0 ) {
|
||||
fprintf(stderr, "%s\n", what);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
initscr();
|
||||
keypad(stdscr, TRUE);
|
||||
scrollok(stdscr, TRUE);
|
||||
|
||||
pthread_create(&reading_thread_id, NULL, reading_thread, NULL);
|
||||
|
||||
while (1) {
|
||||
char message[1024] = { '\0' };
|
||||
int key = getch();
|
||||
switch(key) {
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
snprintf(message, 1024, "capture.data %d",
|
||||
key - '0');
|
||||
break;
|
||||
case 'q':
|
||||
goto finish;
|
||||
default:
|
||||
printw("Unknown key: %d\n", key);
|
||||
}
|
||||
|
||||
if(strlen(message) > 0) {
|
||||
printw("Sent message: \"%s\"\n", message);
|
||||
ssize_t total_written = 0;
|
||||
do {
|
||||
ssize_t ret = write(fd, message, strlen(message)+1 - total_written);
|
||||
if(ret <= 0) {
|
||||
perror("Error sending command");
|
||||
goto finish;
|
||||
}
|
||||
total_written += ret;
|
||||
} while(total_written < (int) strlen(message) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
finish:
|
||||
pthread_cancel(reading_thread_id);
|
||||
pthread_join(reading_thread_id, NULL);
|
||||
|
||||
close(fd);
|
||||
endwin();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#else // ! HAVE_NCURSES
|
||||
#include <stdio.h>
|
||||
int main () { fprintf(stderr, "Recompile with ncurses support!\n"); return 1; }
|
||||
|
||||
#endif // HAVE_NCURSES
|
||||
|
||||
@@ -77,6 +77,7 @@
|
||||
#include "video_capture/screen_osx.h"
|
||||
#include "video_capture/screen_x11.h"
|
||||
#include "video_capture/swmix.h"
|
||||
#include "video_capture/switcher.h"
|
||||
#include "video_capture/testcard.h"
|
||||
#include "video_capture/testcard2.h"
|
||||
#include "video_capture/v4l2.h"
|
||||
@@ -103,6 +104,7 @@ struct vidcap_params {
|
||||
char *name; ///< input name (capture alias in config file or complete config if not alias)
|
||||
struct vidcap_params *next; /**< Pointer to next vidcap params. Used by aggregate capture drivers.
|
||||
* Last device in list has @ref driver set to NULL. */
|
||||
struct module *parent;
|
||||
};
|
||||
|
||||
/// @brief This struct represents video capture state.
|
||||
@@ -165,6 +167,15 @@ struct vidcap_device_api vidcap_device_table[] = {
|
||||
MK_STATIC(vidcap_import_grab),
|
||||
NULL
|
||||
},
|
||||
{
|
||||
0,
|
||||
NULL,
|
||||
MK_STATIC(vidcap_switcher_probe),
|
||||
MK_STATIC(vidcap_switcher_init),
|
||||
MK_STATIC(vidcap_switcher_done),
|
||||
MK_STATIC(vidcap_switcher_grab),
|
||||
NULL
|
||||
},
|
||||
#if defined HAVE_RTSP
|
||||
{
|
||||
0,
|
||||
@@ -457,7 +468,7 @@ vidcap_id_t vidcap_get_null_device_id(void)
|
||||
* @retval <0 if initialization failed
|
||||
* @retval >0 if initialization was successful but no state was returned (eg. only having shown help).
|
||||
*/
|
||||
int vidcap_init(struct module *parent, vidcap_id_t id, const struct vidcap_params *param,
|
||||
int vidcap_init(struct module *parent, vidcap_id_t id, struct vidcap_params *param,
|
||||
struct vidcap **state)
|
||||
{
|
||||
unsigned int i;
|
||||
@@ -467,24 +478,28 @@ int vidcap_init(struct module *parent, vidcap_id_t id, const struct vidcap_param
|
||||
struct vidcap *d =
|
||||
(struct vidcap *)malloc(sizeof(struct vidcap));
|
||||
d->magic = VIDCAP_MAGIC;
|
||||
|
||||
module_init_default(&d->mod);
|
||||
d->mod.cls = MODULE_CLASS_CAPTURE;
|
||||
module_register(&d->mod, parent);
|
||||
|
||||
param->parent = &d->mod;
|
||||
d->state = vidcap_device_table[i].func_init(param);
|
||||
d->index = i;
|
||||
if (d->state == NULL) {
|
||||
debug_msg
|
||||
("Unable to start video capture device 0x%08lx\n",
|
||||
id);
|
||||
module_done(&d->mod);
|
||||
free(d);
|
||||
return -1;
|
||||
}
|
||||
if(d->state == &vidcap_init_noerr) {
|
||||
module_done(&d->mod);
|
||||
free(d);
|
||||
return 1;
|
||||
}
|
||||
|
||||
module_init_default(&d->mod);
|
||||
d->mod.cls = MODULE_CLASS_CAPTURE;
|
||||
module_register(&d->mod, parent);
|
||||
|
||||
int ret = capture_filter_init(&d->mod, param->requested_capture_filter,
|
||||
&d->capture_filter);
|
||||
if(ret < 0) {
|
||||
@@ -682,6 +697,11 @@ const char *vidcap_params_get_name(const struct vidcap_params *params)
|
||||
return params->name;
|
||||
}
|
||||
|
||||
struct module *vidcap_params_get_parent(const struct vidcap_params *params)
|
||||
{
|
||||
return params->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates deep copy of @ref vidcap_params structure.
|
||||
*/
|
||||
|
||||
@@ -110,6 +110,8 @@ struct vidcap_type {
|
||||
//video_colour_mode_t colour_mode;
|
||||
};
|
||||
|
||||
struct module;
|
||||
|
||||
/**
|
||||
* @name Vidcap Parameters Handling Functions
|
||||
* @{ */
|
||||
@@ -125,6 +127,7 @@ const char *vidcap_params_get_driver(const struct vidcap_params *param
|
||||
unsigned int vidcap_params_get_flags(const struct vidcap_params *params);
|
||||
const char *vidcap_params_get_fmt(const struct vidcap_params *params);
|
||||
const char *vidcap_params_get_name(const struct vidcap_params *params);
|
||||
struct module *vidcap_params_get_parent(const struct vidcap_params *params);
|
||||
void vidcap_params_set_device(struct vidcap_params *params, const char *config);
|
||||
void vidcap_params_set_capture_filter(struct vidcap_params *params,
|
||||
const char *req_capture_filter);
|
||||
@@ -141,7 +144,7 @@ struct module;
|
||||
struct vidcap;
|
||||
|
||||
int vidcap_init(struct module *parent, vidcap_id_t id,
|
||||
const struct vidcap_params *param, struct vidcap **);
|
||||
struct vidcap_params *param, struct vidcap **);
|
||||
void vidcap_done(struct vidcap *state);
|
||||
struct video_frame *vidcap_grab(struct vidcap *state, struct audio_frame **audio);
|
||||
|
||||
|
||||
@@ -135,6 +135,7 @@ struct vidcap_decklink_state {
|
||||
unsigned int autodetect_mode:1;
|
||||
|
||||
BMDVideoConnection connection;
|
||||
int audio_consumer_levels; ///< 0 false, 1 true, -1 default
|
||||
|
||||
struct timeval t0;
|
||||
};
|
||||
@@ -393,7 +394,7 @@ decklink_help()
|
||||
HRESULT result;
|
||||
|
||||
printf("\nDecklink options:\n");
|
||||
printf("\t-t decklink[:<device_index(indices)>[:<mode>:<colorspace>[:3D][:timecode][:connection=<input>]]\n");
|
||||
printf("\t-t decklink[:<device_index(indices)>[:<mode>:<colorspace>[:3D][:timecode][:connection=<input>]][:audioConsumerLevels={true|false}]\\n");
|
||||
printf("\t\t(You can omit device index, mode and color space provided that your cards supports format autodetection.)\n");
|
||||
|
||||
// Create an IDeckLinkIterator object to enumerate all DeckLink cards in the system
|
||||
@@ -502,6 +503,11 @@ decklink_help()
|
||||
printf("timecode\n");
|
||||
printf("\tTry to synchronize inputs based on timecode (for multiple inputs, eg. tiled 4K)\n");
|
||||
|
||||
printf("audioConsumerLevels\n");
|
||||
printf("\tIf set true the analog audio levels are set to maximum gain on audio input.\n");
|
||||
printf("\tIf set false the selected analog input gain levels are used.\n");
|
||||
|
||||
printf("\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -600,6 +606,14 @@ settings_init(void *state, char *fmt)
|
||||
fprintf(stderr, "[DeckLink] Unrecognized connection %s.\n", connection);
|
||||
return 0;
|
||||
}
|
||||
} else if(strncasecmp(tmp, "audioConsumerLevels=",
|
||||
strlen("audioConsumerLevels=")) == 0) {
|
||||
char *levels = tmp + strlen("audioConsumerLevels=");
|
||||
if (strcasecmp(levels, "false") == 0) {
|
||||
s->audio_consumer_levels = 0;
|
||||
} else {
|
||||
s->audio_consumer_levels = 1;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "[DeckLink] Warning, unrecognized trailing options in init string: %s", tmp);
|
||||
}
|
||||
@@ -791,6 +805,7 @@ vidcap_decklink_init(const struct vidcap_params *params)
|
||||
s->autodetect_mode = FALSE;
|
||||
s->connection = (BMDVideoConnection) 0;
|
||||
s->flags = 0;
|
||||
s->audio_consumer_levels = -1;
|
||||
|
||||
// SET UP device and mode
|
||||
char *tmp_fmt = strdup(vidcap_params_get_fmt(params));
|
||||
@@ -1039,6 +1054,13 @@ vidcap_decklink_init(const struct vidcap_params *params)
|
||||
"Only 1, 2, 8 or 16 are poosible.", audio_capture_channels);
|
||||
goto error;
|
||||
}
|
||||
if (s->audio_consumer_levels != -1) {
|
||||
result = deckLinkConfiguration->SetFlag(bmdDeckLinkConfigAnalogAudioConsumerLevels,
|
||||
s->audio_consumer_levels == 1 ? true : false);
|
||||
if(result != S_OK) {
|
||||
fprintf(stderr, "[DeckLink capture] Unable set input audio consumer levels.\n");
|
||||
}
|
||||
}
|
||||
deckLinkInput->EnableAudioInput(
|
||||
bmdAudioSampleRate48kHz,
|
||||
bmdAudioSampleType16bitInteger,
|
||||
|
||||
@@ -797,7 +797,7 @@ void setup_codecs_and_controls_from_sdp(const char *sdp_filename, void *state) {
|
||||
if(fp == 0){
|
||||
printf("unable to open asset %s", sdp_filename);
|
||||
fclose(fp);
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
fseek(fp, 0, SEEK_END);
|
||||
unsigned long fileSize = ftell(fp);
|
||||
@@ -808,7 +808,7 @@ void setup_codecs_and_controls_from_sdp(const char *sdp_filename, void *state) {
|
||||
|
||||
if(readResult != fileSize){
|
||||
printf("something bad happens, read result != file size");
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
buffer[fileSize] = '\0';
|
||||
|
||||
|
||||
@@ -57,8 +57,6 @@
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <OpenGL/OpenGL.h>
|
||||
#include <OpenGL/gl.h>
|
||||
#include <Carbon/Carbon.h>
|
||||
|
||||
/* prototypes of functions defined in this module */
|
||||
|
||||
190
src/video_capture/switcher.c
Normal file
190
src/video_capture/switcher.c
Normal file
@@ -0,0 +1,190 @@
|
||||
/**
|
||||
* @file video_capture/switcher.c
|
||||
* @author Martin Pulec <pulec@cesnet.cz>
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2014 CESNET z.s.p.o.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, is permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of CESNET nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* 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 "video.h"
|
||||
#include "video_capture.h"
|
||||
|
||||
#include "tv.h"
|
||||
|
||||
#include "video_capture/switcher.h"
|
||||
#include "audio/audio.h"
|
||||
#include "module.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* prototypes of functions defined in this module */
|
||||
static void show_help(void);
|
||||
|
||||
static void show_help()
|
||||
{
|
||||
printf("switcher capture\n");
|
||||
printf("Usage\n");
|
||||
printf("\t-t switcher -t <dev1_config> -t <dev2_config> ....]\n");
|
||||
printf("\t\twhere devn_config is a configuration of device to be switched\n");
|
||||
|
||||
}
|
||||
|
||||
struct vidcap_switcher_state {
|
||||
struct module mod;
|
||||
struct vidcap **devices;
|
||||
int devices_cnt;
|
||||
|
||||
int selected_device;
|
||||
};
|
||||
|
||||
|
||||
struct vidcap_type *
|
||||
vidcap_switcher_probe(void)
|
||||
{
|
||||
struct vidcap_type* vt;
|
||||
|
||||
vt = (struct vidcap_type *) malloc(sizeof(struct vidcap_type));
|
||||
if (vt != NULL) {
|
||||
vt->id = 0x1D3E1956;
|
||||
vt->name = "switcher";
|
||||
vt->description = "Video switcher pseudodevice";
|
||||
}
|
||||
return vt;
|
||||
}
|
||||
|
||||
void *
|
||||
vidcap_switcher_init(const struct vidcap_params *params)
|
||||
{
|
||||
struct vidcap_switcher_state *s;
|
||||
int i;
|
||||
|
||||
printf("vidcap_switcher_init\n");
|
||||
|
||||
s = (struct vidcap_switcher_state *) calloc(1, sizeof(struct vidcap_switcher_state));
|
||||
if(s == NULL) {
|
||||
printf("Unable to allocate switcher capture state\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(vidcap_params_get_fmt(params) && strcmp(vidcap_params_get_fmt(params), "") != 0) {
|
||||
show_help();
|
||||
return &vidcap_init_noerr;
|
||||
}
|
||||
|
||||
s->devices_cnt = 0;
|
||||
const struct vidcap_params *tmp = params;
|
||||
while((tmp = vidcap_params_get_next(tmp))) {
|
||||
if (vidcap_params_get_driver(tmp) != NULL)
|
||||
s->devices_cnt++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
s->devices = calloc(s->devices_cnt, sizeof(struct vidcap *));
|
||||
i = 0;
|
||||
tmp = params;
|
||||
for (int i = 0; i < s->devices_cnt; ++i) {
|
||||
tmp = vidcap_params_get_next(tmp);
|
||||
|
||||
int ret = initialize_video_capture(NULL, tmp, &s->devices[i]);
|
||||
if(ret != 0) {
|
||||
fprintf(stderr, "[switcher] Unable to initialize device %d (%s:%s).\n",
|
||||
i, vidcap_params_get_driver(tmp),
|
||||
vidcap_params_get_fmt(tmp));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
module_init_default(&s->mod);
|
||||
s->mod.cls = MODULE_CLASS_DATA;
|
||||
module_register(&s->mod, vidcap_params_get_parent(params));
|
||||
|
||||
return s;
|
||||
|
||||
error:
|
||||
if(s->devices) {
|
||||
int i;
|
||||
for (i = 0u; i < s->devices_cnt; ++i) {
|
||||
if(s->devices[i]) {
|
||||
vidcap_done(s->devices[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
vidcap_switcher_done(void *state)
|
||||
{
|
||||
struct vidcap_switcher_state *s = (struct vidcap_switcher_state *) state;
|
||||
|
||||
assert(s != NULL);
|
||||
|
||||
if (s != NULL) {
|
||||
int i;
|
||||
for (i = 0; i < s->devices_cnt; ++i) {
|
||||
vidcap_done(s->devices[i]);
|
||||
}
|
||||
}
|
||||
module_done(&s->mod);
|
||||
free(s);
|
||||
}
|
||||
|
||||
struct video_frame *
|
||||
vidcap_switcher_grab(void *state, struct audio_frame **audio)
|
||||
{
|
||||
struct vidcap_switcher_state *s = (struct vidcap_switcher_state *) state;
|
||||
struct audio_frame *audio_frame = NULL;
|
||||
struct video_frame *frame = NULL;
|
||||
|
||||
struct message *msg;
|
||||
while ((msg = check_message(&s->mod))) {
|
||||
struct msg_universal *msg_univ = (struct msg_universal *) msg;
|
||||
int new_selected_device = atoi(msg_univ->text);
|
||||
if (new_selected_device >= 0 && new_selected_device < s->devices_cnt)
|
||||
s->selected_device = new_selected_device;
|
||||
free_message(msg);
|
||||
}
|
||||
|
||||
frame = vidcap_grab(s->devices[s->selected_device], &audio_frame);
|
||||
*audio = audio_frame;;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
42
src/video_capture/switcher.h
Normal file
42
src/video_capture/switcher.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @file video_capture/switcher.h
|
||||
* @author Martin Pulec <pulec@cesnet.cz>
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2014 CESNET z.s.p.o.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, is permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of CESNET nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
struct vidcap_type *vidcap_switcher_probe(void);
|
||||
void *vidcap_switcher_init(const struct vidcap_params *params);
|
||||
void vidcap_switcher_done(void *state);
|
||||
struct video_frame *vidcap_switcher_grab(void *state, struct audio_frame **audio);
|
||||
|
||||
@@ -78,18 +78,17 @@ void print_fps(int fd, struct v4l2_frmivalenum *param);
|
||||
|
||||
#define DEFAULT_DEVICE "/dev/video0"
|
||||
|
||||
#define BUF_COUNT 2
|
||||
#define DEFAULT_BUF_COUNT 2
|
||||
#define MAX_BUF_COUNT 30
|
||||
|
||||
struct vidcap_v4l2_state {
|
||||
struct video_frame *frame;
|
||||
struct tile *tile;
|
||||
struct video_desc desc;
|
||||
|
||||
int fd;
|
||||
struct {
|
||||
void *start;
|
||||
size_t length;
|
||||
} buffers[BUF_COUNT];
|
||||
struct v4l2_buffer v4l2_dequeued_buffer;
|
||||
} buffers[MAX_BUF_COUNT];
|
||||
|
||||
bool conversion_needed;
|
||||
struct v4lconvert_data *convert;
|
||||
@@ -135,9 +134,10 @@ static void show_help()
|
||||
{
|
||||
printf("V4L2 capture\n");
|
||||
printf("Usage\n");
|
||||
printf("\t-t v4l2[:<dev>[:<pixel_fmt>:[<width>:<height>[:<tpf>]]]]\n");
|
||||
printf("\t-t v4l2[:dev=<dev>][:fmt=<pixel_fmt>][:size=<width>x<height>][:tpf=<tpf>][:buffers=<bufcnt>]\n");
|
||||
printf("\t\tuse device <dev> for grab (default: %s)\n", DEFAULT_DEVICE);
|
||||
printf("\t\t<tpf> - time per frame in format <numerator>/<denominator>\n");
|
||||
printf("\t\t<bufcnt> - number of capture buffers to be used (default: %d)\n", DEFAULT_BUF_COUNT);
|
||||
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
char name[32];
|
||||
@@ -254,6 +254,7 @@ void * vidcap_v4l2_init(const struct vidcap_params *params)
|
||||
height = 0;
|
||||
uint32_t numerator = 0,
|
||||
denominator = 0;
|
||||
int buffer_count = DEFAULT_BUF_COUNT;
|
||||
|
||||
printf("vidcap_v4l2_init\n");
|
||||
|
||||
@@ -276,47 +277,47 @@ void * vidcap_v4l2_init(const struct vidcap_params *params)
|
||||
char *init_fmt = tmp;
|
||||
char *save_ptr = NULL;
|
||||
char *item;
|
||||
int i = 0;
|
||||
while((item = strtok_r(init_fmt, ":", &save_ptr))) {
|
||||
int len;
|
||||
switch (i) {
|
||||
case 0:
|
||||
dev_name = item;
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
if (strncmp(item, "dev=",
|
||||
strlen("dev=")) == 0) {
|
||||
dev_name = item + strlen("dev=");
|
||||
} else if (strncmp(item, "fmt=",
|
||||
strlen("fmt=")) == 0) {
|
||||
char *fmt = item + strlen("fmt=");
|
||||
union {
|
||||
uint32_t fourcc;
|
||||
char str[4];
|
||||
} str_to_uint;
|
||||
len = 4;
|
||||
if(strlen(item) < 4) len = strlen(item);
|
||||
int len = 4;
|
||||
if(strlen(fmt) < 4) len = strlen(fmt);
|
||||
memset(str_to_uint.str, 0, 4);
|
||||
memcpy(str_to_uint.str, item, len);
|
||||
memcpy(str_to_uint.str, fmt, len);
|
||||
pixelformat = str_to_uint.fourcc;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
width = atoi(item);
|
||||
break;
|
||||
case 3:
|
||||
height = atoi(item);
|
||||
break;
|
||||
case 4:
|
||||
} else if (strncmp(item, "size=",
|
||||
strlen("size=")) == 0) {
|
||||
if(strchr(item, 'x')) {
|
||||
width = atoi(item + strlen("size="));
|
||||
height = atoi(strchr(item, 'x') + 1);
|
||||
}
|
||||
} else if (strncmp(item, "tpf=",
|
||||
strlen("tpf=")) == 0) {
|
||||
if(strchr(item, '/')) {
|
||||
numerator = atoi(item);
|
||||
numerator = atoi(item + strlen("tpf="));
|
||||
denominator = atoi(strchr(item, '/') + 1);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
} else if (strncmp(item, "buffers=",
|
||||
strlen("buffers=")) == 0) {
|
||||
buffer_count = atoi(item + strlen("buffers="));
|
||||
assert (buffer_count <= MAX_BUF_COUNT);
|
||||
} else {
|
||||
fprintf(stderr, "[V4L2] Invalid configuration argument: %s\n",
|
||||
item);
|
||||
return NULL;
|
||||
}
|
||||
init_fmt = NULL;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
s->frame = NULL;
|
||||
s->fd = open(dev_name, O_RDWR);
|
||||
|
||||
if(s->fd == -1) {
|
||||
@@ -368,31 +369,32 @@ void * vidcap_v4l2_init(const struct vidcap_params *params)
|
||||
goto error_fd;
|
||||
}
|
||||
|
||||
if(pixelformat) {
|
||||
if (pixelformat) {
|
||||
fmt.fmt.pix.pixelformat = pixelformat;
|
||||
}
|
||||
|
||||
if(width != 0 && height != 0) {
|
||||
fmt.fmt.pix.width = width;
|
||||
fmt.fmt.pix.height = height;
|
||||
}
|
||||
fmt.fmt.pix.field = V4L2_FIELD_ANY;
|
||||
fmt.fmt.pix.bytesperline = 0;
|
||||
if(width != 0 && height != 0) {
|
||||
fmt.fmt.pix.width = width;
|
||||
fmt.fmt.pix.height = height;
|
||||
}
|
||||
|
||||
fmt.fmt.pix.field = V4L2_FIELD_ANY;
|
||||
fmt.fmt.pix.bytesperline = 0;
|
||||
|
||||
if(ioctl(s->fd, VIDIOC_S_FMT, &fmt) != 0) {
|
||||
perror("[V4L2] Unable to set video formant");
|
||||
goto error_fd;
|
||||
}
|
||||
|
||||
if(numerator != 0 && denominator != 0) {
|
||||
stream_params.parm.capture.timeperframe.numerator = numerator;
|
||||
stream_params.parm.capture.timeperframe.denominator = denominator;
|
||||
|
||||
if(ioctl(s->fd, VIDIOC_S_PARM, &stream_params) != 0) {
|
||||
perror("[V4L2] Unable to set stream params");
|
||||
|
||||
if(ioctl(s->fd, VIDIOC_S_FMT, &fmt) != 0) {
|
||||
perror("[V4L2] Unable to set video formant");
|
||||
goto error_fd;
|
||||
}
|
||||
|
||||
if(numerator != 0 && denominator != 0) {
|
||||
stream_params.parm.capture.timeperframe.numerator = numerator;
|
||||
stream_params.parm.capture.timeperframe.denominator = denominator;
|
||||
|
||||
if(ioctl(s->fd, VIDIOC_S_PARM, &stream_params) != 0) {
|
||||
perror("[V4L2] Unable to set stream params");
|
||||
|
||||
goto error_fd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(&s->src_fmt, &fmt, sizeof(fmt));
|
||||
@@ -410,49 +412,48 @@ void * vidcap_v4l2_init(const struct vidcap_params *params)
|
||||
goto error_fd;
|
||||
}
|
||||
|
||||
s->frame = vf_alloc(1);
|
||||
s->tile = vf_get_tile(s->frame, 0);
|
||||
s->desc.tile_count = 1;
|
||||
|
||||
s->conversion_needed = false;
|
||||
|
||||
switch(fmt.fmt.pix.pixelformat) {
|
||||
case V4L2_PIX_FMT_YUYV:
|
||||
s->frame->color_spec = YUYV;
|
||||
s->desc.color_spec = YUYV;
|
||||
break;
|
||||
case V4L2_PIX_FMT_UYVY:
|
||||
s->frame->color_spec = UYVY;
|
||||
s->desc.color_spec = UYVY;
|
||||
break;
|
||||
case V4L2_PIX_FMT_RGB24:
|
||||
s->frame->color_spec = RGB;
|
||||
s->desc.color_spec = RGB;
|
||||
break;
|
||||
case V4L2_PIX_FMT_RGB32:
|
||||
s->frame->color_spec = RGBA;
|
||||
s->desc.color_spec = RGBA;
|
||||
break;
|
||||
case V4L2_PIX_FMT_MJPEG:
|
||||
s->frame->color_spec = MJPG;
|
||||
s->desc.color_spec = MJPG;
|
||||
break;
|
||||
case V4L2_PIX_FMT_H264:
|
||||
s->frame->color_spec = H264;
|
||||
s->desc.color_spec = H264;
|
||||
break;
|
||||
default:
|
||||
s->conversion_needed = true;
|
||||
s->dst_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
|
||||
s->frame->color_spec = RGB;
|
||||
s->desc.color_spec = RGB;
|
||||
break;
|
||||
}
|
||||
|
||||
switch(fmt.fmt.pix.field) {
|
||||
case V4L2_FIELD_NONE:
|
||||
s->frame->interlacing = PROGRESSIVE;
|
||||
s->desc.interlacing = PROGRESSIVE;
|
||||
break;
|
||||
case V4L2_FIELD_TOP:
|
||||
s->frame->interlacing = UPPER_FIELD_FIRST;
|
||||
s->desc.interlacing = UPPER_FIELD_FIRST;
|
||||
break;
|
||||
case V4L2_FIELD_BOTTOM:
|
||||
s->frame->interlacing = LOWER_FIELD_FIRST;
|
||||
s->desc.interlacing = LOWER_FIELD_FIRST;
|
||||
break;
|
||||
case V4L2_FIELD_INTERLACED:
|
||||
s->frame->interlacing = INTERLACED_MERGED;
|
||||
s->desc.interlacing = INTERLACED_MERGED;
|
||||
break;
|
||||
case V4L2_FIELD_SEQ_TB:
|
||||
case V4L2_FIELD_SEQ_BT:
|
||||
@@ -461,18 +462,14 @@ void * vidcap_v4l2_init(const struct vidcap_params *params)
|
||||
case V4L2_FIELD_INTERLACED_BT:
|
||||
default:
|
||||
fprintf(stderr, "[V4L2] Unsupported interlacing format reported from driver.\n");
|
||||
goto free_frame;
|
||||
goto error_fd;
|
||||
}
|
||||
s->frame->fps = (double) stream_params.parm.capture.timeperframe.denominator /
|
||||
s->desc.fps = (double) stream_params.parm.capture.timeperframe.denominator /
|
||||
stream_params.parm.capture.timeperframe.numerator;
|
||||
s->tile->width = fmt.fmt.pix.width;
|
||||
s->tile->height = fmt.fmt.pix.height;
|
||||
s->desc.width = fmt.fmt.pix.width;
|
||||
s->desc.height = fmt.fmt.pix.height;
|
||||
|
||||
if(s->conversion_needed) {
|
||||
s->tile->data_len = vc_get_linesize(s->tile->width, s->frame->color_spec) *
|
||||
s->tile->height;
|
||||
s->tile->data = malloc(s->tile->data_len);
|
||||
s->frame->data_deleter = vf_data_deleter;
|
||||
s->convert = v4lconvert_create(s->fd);
|
||||
} else {
|
||||
s->convert = NULL;
|
||||
@@ -483,21 +480,21 @@ void * vidcap_v4l2_init(const struct vidcap_params *params)
|
||||
memset(&reqbuf, 0, sizeof(reqbuf));
|
||||
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
reqbuf.memory = V4L2_MEMORY_MMAP;
|
||||
reqbuf.count = BUF_COUNT;
|
||||
reqbuf.count = buffer_count;
|
||||
|
||||
if (ioctl (s->fd, VIDIOC_REQBUFS, &reqbuf) != 0) {
|
||||
if (errno == EINVAL)
|
||||
printf("Video capturing or mmap-streaming is not supported\n");
|
||||
else
|
||||
perror("VIDIOC_REQBUFS");
|
||||
goto free_frame;
|
||||
goto error_fd;
|
||||
|
||||
}
|
||||
|
||||
if (reqbuf.count < 2) {
|
||||
/* You may need to free the buffers here. */
|
||||
printf("Not enough buffer memory\n");
|
||||
goto free_frame;
|
||||
goto error_fd;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < reqbuf.count; i++) {
|
||||
@@ -509,7 +506,7 @@ void * vidcap_v4l2_init(const struct vidcap_params *params)
|
||||
|
||||
if (-1 == ioctl (s->fd, VIDIOC_QUERYBUF, &buf)) {
|
||||
perror("VIDIOC_QUERYBUF");
|
||||
goto free_frame;
|
||||
goto error_fd;
|
||||
}
|
||||
|
||||
s->buffers[i].length = buf.length; /* remember for munmap() */
|
||||
@@ -523,24 +520,20 @@ void * vidcap_v4l2_init(const struct vidcap_params *params)
|
||||
/* If you do not exit here you should unmap() and free()
|
||||
the buffers mapped so far. */
|
||||
perror("mmap");
|
||||
goto free_frame;
|
||||
goto error_fd;
|
||||
}
|
||||
|
||||
buf.flags = 0;
|
||||
|
||||
if (i < reqbuf.count - 1) {
|
||||
if(ioctl(s->fd, VIDIOC_QBUF, &buf) != 0) {
|
||||
perror("Unable to enqueue buffer");
|
||||
goto free_frame;
|
||||
}
|
||||
} else {
|
||||
memcpy(&s->v4l2_dequeued_buffer, &buf, sizeof(buf));
|
||||
if(ioctl(s->fd, VIDIOC_QBUF, &buf) != 0) {
|
||||
perror("Unable to enqueue buffer");
|
||||
goto error_fd;
|
||||
}
|
||||
}
|
||||
|
||||
if(ioctl(s->fd, VIDIOC_STREAMON, &reqbuf.type) != 0) {
|
||||
perror("Unable to start stream");
|
||||
goto free_frame;
|
||||
goto error_fd;
|
||||
};
|
||||
|
||||
gettimeofday(&s->t0, NULL);
|
||||
@@ -550,8 +543,6 @@ void * vidcap_v4l2_init(const struct vidcap_params *params)
|
||||
|
||||
return s;
|
||||
|
||||
free_frame:
|
||||
vf_free(s->frame);
|
||||
error_fd:
|
||||
close(s->fd);
|
||||
free(s);
|
||||
@@ -569,8 +560,6 @@ void vidcap_v4l2_done(void *state)
|
||||
|
||||
close(s->fd);
|
||||
|
||||
vf_free(s->frame);
|
||||
|
||||
if(s->conversion_needed) {
|
||||
v4lconvert_destroy(s->convert);
|
||||
}
|
||||
@@ -578,16 +567,33 @@ void vidcap_v4l2_done(void *state)
|
||||
free(s);
|
||||
}
|
||||
|
||||
struct v4l2_dispose_deq_buffer_data {
|
||||
int fd;
|
||||
struct v4l2_buffer buf;
|
||||
};
|
||||
|
||||
static void vidcap_v4l2_dispose_video_frame(struct video_frame *frame) {
|
||||
struct v4l2_dispose_deq_buffer_data *data =
|
||||
(struct v4l2_dispose_deq_buffer_data *) frame->dispose_udata;
|
||||
if (data) {
|
||||
if (ioctl(data->fd, VIDIOC_QBUF, &data->buf) != 0) {
|
||||
perror("Unable to enqueue buffer");
|
||||
};
|
||||
free(data);
|
||||
} else {
|
||||
free(frame->tiles[0].data);
|
||||
}
|
||||
|
||||
vf_free(frame);
|
||||
}
|
||||
|
||||
struct video_frame * vidcap_v4l2_grab(void *state, struct audio_frame **audio)
|
||||
{
|
||||
struct vidcap_v4l2_state *s = (struct vidcap_v4l2_state *) state;
|
||||
struct video_frame *out;
|
||||
|
||||
*audio = NULL;
|
||||
|
||||
if (ioctl(s->fd, VIDIOC_QBUF, &s->v4l2_dequeued_buffer) != 0) {
|
||||
perror("Unable to enqueue buffer");
|
||||
};
|
||||
|
||||
struct v4l2_buffer buf;
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
@@ -595,27 +601,42 @@ struct video_frame * vidcap_v4l2_grab(void *state, struct audio_frame **audio)
|
||||
|
||||
if(ioctl(s->fd, VIDIOC_DQBUF, &buf) != 0) {
|
||||
perror("Unable to dequeue buffer");
|
||||
return NULL;
|
||||
};
|
||||
|
||||
memcpy(&s->v4l2_dequeued_buffer, &buf, sizeof(buf));
|
||||
out = vf_alloc_desc(s->desc);
|
||||
out->dispose = vidcap_v4l2_dispose_video_frame;
|
||||
|
||||
if(!s->conversion_needed) {
|
||||
s->tile->data = s->buffers[buf.index].start;
|
||||
s->tile->data_len = buf.bytesused;
|
||||
struct v4l2_dispose_deq_buffer_data *frame_data =
|
||||
malloc(sizeof(struct v4l2_dispose_deq_buffer_data));
|
||||
frame_data->fd = s->fd;
|
||||
memcpy(&frame_data->buf, &buf, sizeof(buf));
|
||||
out->tiles[0].data = s->buffers[frame_data->buf.index].start;
|
||||
out->tiles[0].data_len = frame_data->buf.bytesused;
|
||||
out->dispose_udata = frame_data;
|
||||
} else {
|
||||
out->dispose_udata = NULL;
|
||||
out->tiles[0].data = (char *) malloc(out->tiles[0].data_len);
|
||||
int ret = v4lconvert_convert(s->convert,
|
||||
&s->src_fmt, /* in */
|
||||
&s->dst_fmt, /* in */
|
||||
s->buffers[buf.index].start,
|
||||
buf.bytesused,
|
||||
(unsigned char *) s->tile->data,
|
||||
s->tile->data_len);
|
||||
(unsigned char *) out->tiles[0].data,
|
||||
out->tiles[0].data_len);
|
||||
|
||||
// we do not need the driver buffer any more
|
||||
if (ioctl(s->fd, VIDIOC_QBUF, &buf) != 0) {
|
||||
perror("Unable to enqueue buffer");
|
||||
};
|
||||
|
||||
if(ret == -1) {
|
||||
fprintf(stderr, "Error converting video.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s->tile->data_len = ret;
|
||||
out->tiles[0].data_len = ret;
|
||||
}
|
||||
|
||||
s->frames++;
|
||||
@@ -631,6 +652,6 @@ struct video_frame * vidcap_v4l2_grab(void *state, struct audio_frame **audio)
|
||||
}
|
||||
|
||||
|
||||
return s->frame;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@@ -149,13 +149,13 @@ static int parse_fmt(struct state_video_compress_libav *s, char *fmt);
|
||||
static void cleanup(struct state_video_compress_libav *s);
|
||||
|
||||
static void libavcodec_vid_enc_frame_dispose(struct video_frame *frame) {
|
||||
#ifdef HAVE_AVCODEC_ENCODE_VIDEO2
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 54
|
||||
AVPacket *pkt = (AVPacket *) frame->dispose_udata;
|
||||
av_free_packet(pkt);
|
||||
free(pkt);
|
||||
#else
|
||||
free(frame->tiles[0].data);
|
||||
#endif // HAVE_AVCODEC_ENCODE_VIDEO2
|
||||
#endif // LIBAVCODEC_VERSION_MAJOR >= 54
|
||||
vf_free(frame);
|
||||
}
|
||||
|
||||
@@ -583,7 +583,7 @@ struct video_frame *libavcodec_compress_tile(struct module *mod, struct video_fr
|
||||
struct state_video_compress_libav *s = (struct state_video_compress_libav *) mod->priv_data;
|
||||
static int frame_seq = 0;
|
||||
int ret;
|
||||
#ifdef HAVE_AVCODEC_ENCODE_VIDEO2
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 54
|
||||
int got_output;
|
||||
#endif
|
||||
unsigned char *decoded;
|
||||
@@ -601,7 +601,7 @@ struct video_frame *libavcodec_compress_tile(struct module *mod, struct video_fr
|
||||
|
||||
struct video_frame *out = vf_alloc_desc(s->compressed_desc);
|
||||
out->dispose = libavcodec_vid_enc_frame_dispose;
|
||||
#ifdef HAVE_AVCODEC_ENCODE_VIDEO2
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 54
|
||||
AVPacket *pkt = (AVPacket *) malloc(sizeof(AVPacket));
|
||||
av_init_packet(pkt);
|
||||
pkt->data = NULL;
|
||||
@@ -610,7 +610,7 @@ struct video_frame *libavcodec_compress_tile(struct module *mod, struct video_fr
|
||||
#else
|
||||
out->tiles[0].data = malloc(s->compressed_desc.width *
|
||||
s->compressed_desc.height * 4);
|
||||
#endif // HAVE_AVCODEC_ENCODE_VIDEO2
|
||||
#endif // LIBAVCODEC_VERSION_MAJOR >= 54
|
||||
|
||||
|
||||
s->in_frame->pts = frame_seq++;
|
||||
@@ -657,7 +657,7 @@ struct video_frame *libavcodec_compress_tile(struct module *mod, struct video_fr
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_AVCODEC_ENCODE_VIDEO2
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 54
|
||||
/* encode the image */
|
||||
ret = avcodec_encode_video2(s->codec_ctx, pkt,
|
||||
s->in_frame, &got_output);
|
||||
@@ -689,7 +689,7 @@ struct video_frame *libavcodec_compress_tile(struct module *mod, struct video_fr
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
#endif // HAVE_AVCODEC_ENCODE_VIDEO2
|
||||
#endif // LIBAVCODEC_VERSION_MAJOR >= 54
|
||||
|
||||
platform_spin_unlock(&s->spin);
|
||||
|
||||
|
||||
@@ -280,8 +280,8 @@ static void show_help(void)
|
||||
HRESULT result;
|
||||
|
||||
printf("Decklink (output) options:\n");
|
||||
printf("\t-d decklink:<device_number(s)>[:timecode][:3G|:dual-link][:3D[:HDMI3DPacking=<packing>]][:fast]\n");
|
||||
printf("\t\tcoma-separated numbers of output devices\n");
|
||||
printf("\t-d decklink:<device_number(s)>[:timecode][:3G|:dual-link][:3D[:HDMI3DPacking=<packing>]][:fast][:audioConsumerLevels={true|false}]\n");
|
||||
printf("\t\t<device_number(s)> is coma-separated indices of output devices\n");
|
||||
// Create an IDeckLinkIterator object to enumerate all DeckLink cards in the system
|
||||
#ifdef WIN32
|
||||
result = CoCreateInstance(CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL,
|
||||
@@ -352,6 +352,8 @@ static void show_help(void)
|
||||
|
||||
printf("Fast mode has lower latency at the expense of incorrect displaying (frame rewriting).\n");
|
||||
printf("\n");
|
||||
printf("audioConsumerLevels if set to true sets audio analog level to maximum attenuation on audio output.\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -734,6 +736,7 @@ void *display_decklink_init(char *fmt, unsigned int flags)
|
||||
// for Decklink Studio which has switchable XLR - analog 3 and 4 or AES/EBU 3,4 and 5,6
|
||||
BMDAudioOutputAnalogAESSwitch audioConnection = (BMDAudioOutputAnalogAESSwitch) 0;
|
||||
BMDVideo3DPackingFormat HDMI3DPacking = (BMDVideo3DPackingFormat) 0;
|
||||
int audio_consumer_levels = -1;
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -835,6 +838,12 @@ void *display_decklink_init(char *fmt, unsigned int flags)
|
||||
}
|
||||
} else if(strcasecmp(ptr, "fast") == 0) {
|
||||
s->fast = true;
|
||||
} else if(strncasecmp(ptr, "audioConsumerLevels=", strlen("audioConsumerLevels=")) == 0) {
|
||||
if (strcasecmp(ptr + strlen("audioConsumerLevels="), "false") == 0) {
|
||||
audio_consumer_levels = 0;
|
||||
} else {
|
||||
audio_consumer_levels = 1;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "[DeckLink] Warning: unknown options in config string.\n");
|
||||
}
|
||||
@@ -986,17 +995,25 @@ void *display_decklink_init(char *fmt, unsigned int flags)
|
||||
* .... one exception is a card that has switchable cables between AES/EBU and analog. (But this applies only for channels 3 and above.)
|
||||
*/
|
||||
if (audioConnection != 0) { // not embedded
|
||||
HRESULT res = deckLinkConfiguration->SetInt(bmdDeckLinkConfigAudioOutputAESAnalogSwitch,
|
||||
result = deckLinkConfiguration->SetInt(bmdDeckLinkConfigAudioOutputAESAnalogSwitch,
|
||||
audioConnection);
|
||||
if(res == S_OK) { // has switchable channels
|
||||
if(result == S_OK) { // has switchable channels
|
||||
printf("[Decklink playback] Card with switchable audio channels detected. Switched to correct format.\n");
|
||||
} else if(res == E_NOTIMPL) {
|
||||
} else if(result == E_NOTIMPL) {
|
||||
// normal case - without switchable channels
|
||||
} else {
|
||||
fprintf(stderr, "[Decklink playback] Unable to switch audio output for channels 3 or above although \n"
|
||||
"card shall support it. Check if it is ok. Continuing anyway.\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (audio_consumer_levels != -1) {
|
||||
result = deckLinkConfiguration->SetFlag(bmdDeckLinkConfigAnalogAudioConsumerLevels,
|
||||
audio_consumer_levels == 1 ? true : false);
|
||||
if(result != S_OK) {
|
||||
fprintf(stderr, "[DeckLink display] Unable set output audio consumer levels.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s->state[i].delegate = new PlaybackDelegate();
|
||||
@@ -1159,6 +1176,7 @@ void display_decklink_put_audio_frame(void *state, struct audio_frame *frame)
|
||||
tmp_frame.max_size = sampleFrameCount * s->output_audio_channel_count
|
||||
* frame->bps;
|
||||
tmp_frame.data = (char *) malloc(tmp_frame.max_size);
|
||||
memcpy(tmp_frame.data, frame->data, frame->data_len);
|
||||
|
||||
audio_frame_multiply_channel(&tmp_frame,
|
||||
s->output_audio_channel_count);
|
||||
|
||||
@@ -91,8 +91,8 @@
|
||||
#include "video_display/splashscreen.h"
|
||||
#include "tv.h"
|
||||
|
||||
#define MAGIC_GL DISPLAY_GL_ID
|
||||
#define WIN_NAME "Ultragrid - OpenGL Display"
|
||||
#define MAGIC_GL DISPLAY_GL_ID
|
||||
#define DEFAULT_WIN_NAME "Ultragrid - OpenGL Display"
|
||||
|
||||
#define STRINGIFY(A) #A
|
||||
|
||||
@@ -264,6 +264,7 @@ static void gl_load_splashscreen(struct state_gl *s)
|
||||
gl_reconfigure_screen(s);
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
const char *data = splash_data;
|
||||
memset(s->buffers[i], 0, s->tile->data_len);
|
||||
for (unsigned int y = 0; y < splash_height; ++y) {
|
||||
char *line = s->buffers[i];
|
||||
@@ -274,7 +275,7 @@ static void gl_load_splashscreen(struct state_gl *s)
|
||||
(s->tile->width - splash_width)/2,
|
||||
s->frame->color_spec);
|
||||
for (unsigned int x = 0; x < splash_width; ++x) {
|
||||
HEADER_PIXEL(splash_data,line);
|
||||
HEADER_PIXEL(data,line);
|
||||
line += 4;
|
||||
}
|
||||
}
|
||||
@@ -383,7 +384,7 @@ void * display_gl_init(char *fmt, unsigned int flags) {
|
||||
glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION);
|
||||
#endif
|
||||
glutIdleFunc(glut_idle_callback);
|
||||
s->window = glutCreateWindow(WIN_NAME);
|
||||
s->window = glutCreateWindow(window_title != NULL ? window_title : DEFAULT_WIN_NAME);
|
||||
glutSetCursor(s->show_cursor ? GLUT_CURSOR_CROSSHAIR : GLUT_CURSOR_NONE);
|
||||
//glutHideWindow();
|
||||
glutKeyboardFunc(glut_key_callback);
|
||||
@@ -489,13 +490,15 @@ int display_gl_reconfigure(void *state, struct video_desc desc)
|
||||
|
||||
static void glut_resize_window(struct state_gl *s)
|
||||
{
|
||||
if (!s->fs) {
|
||||
if (s->fs) {
|
||||
glutReshapeWindow(glutGet(GLUT_SCREEN_WIDTH),
|
||||
glutGet(GLUT_SCREEN_HEIGHT));
|
||||
glutFullScreen();
|
||||
} else {
|
||||
glutReshapeWindow(s->window_size_factor *
|
||||
s->tile->height * s->aspect,
|
||||
s->window_size_factor *
|
||||
s->tile->height);
|
||||
} else {
|
||||
glutFullScreen();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -877,7 +880,7 @@ static void gl_resize(int width,int height)
|
||||
// redraw last frame
|
||||
gl_render(gl);
|
||||
gl_draw(gl->aspect);
|
||||
glutPostRedisplay();
|
||||
glutSwapBuffers();
|
||||
}
|
||||
|
||||
static void gl_bind_texture(void *arg)
|
||||
|
||||
@@ -168,10 +168,11 @@ static void loadSplashscreen(struct state_sdl *s) {
|
||||
#endif
|
||||
|
||||
// load splash data
|
||||
const char *data = splash_data;
|
||||
for (y_coord = 0; y_coord < splash_height; y_coord++) {
|
||||
for (x_coord = 0; x_coord < splash_width; x_coord++) {
|
||||
|
||||
HEADER_PIXEL(splash_data,pixel);
|
||||
HEADER_PIXEL(data,pixel);
|
||||
Uint32 color = SDL_MapRGB(image->format, pixel[0], pixel[1], pixel[2]);
|
||||
|
||||
switch(image->format->BytesPerPixel) {
|
||||
@@ -472,7 +473,11 @@ int display_sdl_reconfigure(void *state, struct video_desc desc)
|
||||
x_res_y);
|
||||
return FALSE;
|
||||
}
|
||||
SDL_WM_SetCaption("Ultragrid - SDL Display", "Ultragrid");
|
||||
if (window_title) {
|
||||
SDL_WM_SetCaption(window_title, window_title);
|
||||
} else {
|
||||
SDL_WM_SetCaption("Ultragrid - SDL Display", "Ultragrid");
|
||||
}
|
||||
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* GIMP header image file format (RGB) */
|
||||
|
||||
static unsigned int splash_width = 339;
|
||||
static unsigned int splash_height = 195;
|
||||
static const unsigned int splash_width = 339;
|
||||
static const unsigned int splash_height = 195;
|
||||
|
||||
/* Call this macro repeatedly. After each use, the pixel data can be extracted */
|
||||
|
||||
@@ -11,7 +11,7 @@ pixel[1] = ((((data[1] - 33) & 0xF) << 4) | ((data[2] - 33) >> 2)); \
|
||||
pixel[2] = ((((data[2] - 33) & 0x3) << 6) | ((data[3] - 33))); \
|
||||
data += 4; \
|
||||
}
|
||||
static char *splash_data =
|
||||
static const char * const splash_data =
|
||||
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
|
||||
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
|
||||
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
|
||||
|
||||
@@ -183,6 +183,8 @@ void *video_rxtx::sender_loop() {
|
||||
|
||||
if (!m_paused) {
|
||||
send_frame(tx_frame);
|
||||
} else {
|
||||
VIDEO_FRAME_DISPOSE(tx_frame);
|
||||
}
|
||||
|
||||
if (dynamic_cast<rtp_video_rxtx *>(this)) {
|
||||
|
||||
Reference in New Issue
Block a user