Files
UltraGrid/src/audio/playback/decklink.cpp
Martin Pulec 06d89598fa Fixed some Coverity reported errors
These Coverity errors should be fixed (mostly classified as High
Impact Outstanding by Coverity):
53922, 53923, 53935, 53938, 53945, 53963, 53974, 53976, 53978, 53979,
53980, 53981, 53982, 53988, 53990, 53991, 53993, 53994, 53995, 53998,
54000, 54002, 54004, 54008, 54010, 54013, 54016, 54018, 54020, 54022,
54024, 54025, 54027, 54028, 54030, 54031, 54032, 54033, 54034, 54035,
54036, 54038, 54039, 54041, 54043, 54047, 54048, 54050, 54053, 54054,
54055, 54056, 54062, 54064, 54065, 54067, 54071, 54072, 54074, 54075,
54076, 54081, 54087, 54088, 54089, 54091, 54093, 54096, 54097, 54103,
54104, 54106, 54109, 54111, 54112, 54114, 54117, 54119, 54120, 54121,
54125, 54126, 54129, 54130, 54132, 54133, 54136, 54137, 54140, 54145,
54146, 54149, 54150, 54151, 54153, 54154, 54156, 54157
2014-09-08 15:29:02 +02:00

554 lines
21 KiB
C++

/*
* FILE: video_display/decklink.cpp
* AUTHORS: Martin Benes <martinbenesh@gmail.com>
* Lukas Hejtmanek <xhejtman@ics.muni.cz>
* Petr Holub <hopet@ics.muni.cz>
* Milos Liska <xliska@fi.muni.cz>
* Jiri Matela <matela@ics.muni.cz>
* Dalibor Matura <255899@mail.muni.cz>
* Ian Wesley-Smith <iwsmith@cct.lsu.edu>
* Colin Perkins <csp@isi.edu>
*
* Copyright (c) 2005-2010 CESNET z.s.p.o.
* Copyright (c) 2001-2003 University of Southern California
*
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
*
* This product includes software developed by the University of Southern
* California Information Sciences Institute. This product also includes
* software developed by CESNET z.s.p.o.
*
* 4. Neither the name of the University, Institute, 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 "debug.h"
#include "config.h"
#include "config_unix.h"
#include "config_win32.h"
#include "video_codec.h"
#include "tv.h"
#include "audio/playback/decklink.h"
#include "debug.h"
#include "video_capture.h"
#include "audio/audio.h"
#include "audio/utils.h"
#ifdef WIN32
#include "DeckLinkAPI_h.h"
#else
#include "DeckLinkAPI.h"
#endif
#include "DeckLinkAPIVersion.h"
#ifdef WIN32
#include <objbase.h>
#endif
#ifdef HAVE_MACOSX
#define STRING CFStringRef
#elif defined WIN32
#define STRING BSTR
#else
#define STRING const char *
#endif
#ifndef WIN32
#define STDMETHODCALLTYPE
#endif
// defined int video_capture/decklink.cpp
void print_output_modes(IDeckLink *);
static int blackmagic_api_version_check(STRING *current_version);
namespace {
class PlaybackDelegate;
}
#define DECKLINK_MAGIC DISPLAY_DECKLINK_ID
struct state_decklink {
uint32_t magic;
struct audio_desc audio_desc;
int output_audio_channel_count;
PlaybackDelegate *delegate;
IDeckLink *deckLink;
IDeckLinkOutput *deckLinkOutput;
IDeckLinkMutableVideoFrame *deckLinkFrame;
BMDTimeValue frameRateDuration;
BMDTimeScale frameRateScale;
int frames;
int audio_consumer_levels; ///< 0 false, 1 true, -1 default
};
namespace {
class PlaybackDelegate : public IDeckLinkVideoOutputCallback // , public IDeckLinkAudioOutputCallback
{
struct state_decklink * s;
public:
PlaybackDelegate (struct state_decklink* owner) {
s = owner;
}
// IUnknown needs only a dummy implementation
virtual HRESULT STDMETHODCALLTYPE QueryInterface (REFIID, LPVOID *) { return E_NOINTERFACE;}
virtual ULONG STDMETHODCALLTYPE AddRef () {return 1;}
virtual ULONG STDMETHODCALLTYPE Release () {return 1;}
virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted (IDeckLinkVideoFrame *, BMDOutputFrameCompletionResult) {
s->deckLinkOutput->ScheduleVideoFrame(s->deckLinkFrame,
s->frames * s->frameRateDuration, s->frameRateDuration, s->frameRateScale);
s->frames++;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped () { return S_OK; }
//virtual HRESULT RenderAudioSamples (bool preroll);
};
} // end of unnamed namespace
static int blackmagic_api_version_check(STRING *current_version)
{
int ret = TRUE;
*current_version = NULL;
IDeckLinkAPIInformation *APIInformation = NULL;
HRESULT result;
#ifdef WIN32
result = CoCreateInstance(CLSID_CDeckLinkAPIInformation, NULL, CLSCTX_ALL,
IID_IDeckLinkAPIInformation, (void **) &APIInformation);
if(FAILED(result))
#else
APIInformation = CreateDeckLinkAPIInformationInstance();
if(APIInformation == NULL)
#endif
{
return FALSE;
}
int64_t value;
result = APIInformation->GetInt(BMDDeckLinkAPIVersion, &value);
if(result != S_OK) {
APIInformation->Release();
return FALSE;
}
if(BLACKMAGIC_DECKLINK_API_VERSION > value) { // this is safe comparision, for internal structure please see SDK documentation
APIInformation->GetString(BMDDeckLinkAPIVersion, current_version);
ret = FALSE;
}
APIInformation->Release();
return ret;
}
void decklink_playback_help(const char *driver_name)
{
IDeckLinkIterator* deckLinkIterator;
IDeckLink* deckLink;
int numDevices = 0;
HRESULT result;
UNUSED(driver_name);
// Create an IDeckLinkIterator object to enumerate all DeckLink cards in the system
#ifdef WIN32
result = CoCreateInstance(CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL,
IID_IDeckLinkIterator, (void **) &deckLinkIterator);
if (FAILED(result))
#else
deckLinkIterator = CreateDeckLinkIteratorInstance();
if (deckLinkIterator == NULL)
#endif
{
fprintf(stderr, "\nA DeckLink iterator could not be created. The DeckLink drivers may not be installed or are outdated.\n");
fprintf(stderr, "This UltraGrid version was compiled with DeckLink drivers %s. You should have at least this version.\n\n",
BLACKMAGIC_DECKLINK_API_VERSION_STRING);
return;
}
// Enumerate all cards in this system
while (deckLinkIterator->Next(&deckLink) == S_OK)
{
STRING deviceNameString = NULL;
const char *deviceNameCString;
// *** Print the model name of the DeckLink card
result = deckLink->GetModelName((STRING *) &deviceNameString);
#ifdef HAVE_MACOSX
deviceNameCString = (char *) malloc(128);
CFStringGetCString(deviceNameString, (char *) deviceNameCString, 128, kCFStringEncodingMacRoman);
#elif defined WIN32
deviceNameCString = (char *) malloc(128);
wcstombs((char *) deviceNameCString, deviceNameString, 128);
#else
deviceNameCString = deviceNameString;
#endif
if (result == S_OK)
{
printf("\tdecklink:%d : Blackmagic %s\n",numDevices, deviceNameCString);
#ifdef HAVE_MACOSX
CFRelease(deviceNameString);
#endif
free((void *)deviceNameCString);
}
// Increment the total number of DeckLink cards found
numDevices++;
// Release the IDeckLink instance when we've finished with it to prevent leaks
deckLink->Release();
}
deckLinkIterator->Release();
// If no DeckLink cards were found in the system, inform the user
if (numDevices == 0)
{
printf("\nNo Blackmagic Design devices were found.\n");
return;
}
}
void *decklink_playback_init(char *cfg)
{
struct state_decklink *s = NULL;
IDeckLinkIterator* deckLinkIterator;
HRESULT result;
IDeckLinkConfiguration* deckLinkConfiguration = NULL;
// for Decklink Studio which has switchable XLR - analog 3 and 4 or AES/EBU 3,4 and 5,6
//BMDAudioOutputAnalogAESSwitch audioConnection = (BMDAudioOutputAnalogAESSwitch) 0;
int cardIdx = 0;
int dnum = 0;
IDeckLink *deckLink;
IDeckLinkDisplayModeIterator *displayModeIterator = NULL;
IDeckLinkDisplayMode *deckLinkDisplayMode = NULL;
//BMDDisplayMode displayMode = bmdModeUnknown;
int width, height;
#ifdef WIN32
// Initialize COM on this thread
result = CoInitialize(NULL);
if(FAILED(result)) {
fprintf(stderr, "Initialization of COM failed - result = "
"08x.\n", result);
return NULL;
}
#endif
STRING current_version;
if(!blackmagic_api_version_check(&current_version)) {
fprintf(stderr, "\nThe DeckLink drivers may not be installed or are outdated.\n");
fprintf(stderr, "This UltraGrid version was compiled against DeckLink drivers %s. You should have at least this version.\n\n",
BLACKMAGIC_DECKLINK_API_VERSION_STRING);
fprintf(stderr, "Vendor download page is http://http://www.blackmagic-design.com/support/ \n");
if(current_version) {
const char *currentVersionCString;
#ifdef HAVE_MACOSX
currentVersionCString = (char *) malloc(128);
CFStringGetCString(current_version, (char *) currentVersionCString, 128, kCFStringEncodingMacRoman);
#elif defined WIN32
currentVersionCString = (char *) malloc(128);
wcstombs((char *) currentVersionCString, current_version, 128);
#else
currentVersionCString = current_version;
#endif
fprintf(stderr, "Currently installed version is: %s\n", currentVersionCString);
#ifdef HAVE_MACOSX
CFRelease(current_version);
#endif
free((void *)currentVersionCString);
} else {
fprintf(stderr, "No installed drivers detected\n");
}
fprintf(stderr, "\n");
return NULL;
}
s = (struct state_decklink *)calloc(1, sizeof(struct state_decklink));
s->magic = DECKLINK_MAGIC;
s->audio_consumer_levels = -1;
if (cfg == NULL) {
cardIdx = 0;
fprintf(stderr, "Card number unset, using first found (see -r decklink:help)!\n");
} else if (strcmp(cfg, "help") == 0) {
printf("Available Blackmagic audio playback devices:\n");
decklink_playback_help(NULL);
printf("Options:\n");
printf("\t-r decklink[:<index>][:audioConsumerLevels={true|false}]\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");
return NULL;
} else {
char *tmp = strdup(cfg);
char *item, *save_ptr;
item = strtok_r(tmp, ":", &save_ptr);
if (item) {
if(strncasecmp(item, "audioConsumerLevels=",
strlen("audioConsumerLevels=")) == 0) {
char *levels = item + strlen("audioConsumerLevels=");
if (strcasecmp(levels, "false") == 0) {
s->audio_consumer_levels = 0;
} else {
s->audio_consumer_levels = 1;
}
item = strtok_r(NULL, ":", &save_ptr);
if (item) {
cardIdx = atoi(cfg);
}
} else {
cardIdx = atoi(cfg);
}
}
free(tmp);
}
// Initialize the DeckLink API
#ifdef WIN32
result = CoCreateInstance(CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL,
IID_IDeckLinkIterator, (void **) &deckLinkIterator);
if (FAILED(result))
#else
deckLinkIterator = CreateDeckLinkIteratorInstance();
if (!deckLinkIterator)
#endif
{
fprintf(stderr, "\nA DeckLink iterator could not be created. The DeckLink drivers may not be installed or are outdated.\n");
fprintf(stderr, "This UltraGrid version was compiled with DeckLink drivers %s. You should have at least this version.\n\n",
BLACKMAGIC_DECKLINK_API_VERSION_STRING);
goto error;
}
// Connect to the first DeckLink instance
while (deckLinkIterator->Next(&deckLink) == S_OK)
{
if (dnum == cardIdx){
s->deckLink = deckLink;
} else {
deckLink->Release();
}
dnum++;
}
if(s->deckLink == NULL) {
fprintf(stderr, "No DeckLink PCI card #%d found\n", cardIdx);
goto error;
}
// Obtain the audio/video output interface (IDeckLinkOutput)
if (s->deckLink->QueryInterface(IID_IDeckLinkOutput, (void**)&s->deckLinkOutput) != S_OK) {
goto error;
}
// Query the DeckLink for its configuration interface
result = s->deckLink->QueryInterface(IID_IDeckLinkConfiguration, (void**)&deckLinkConfiguration);
if (result != S_OK)
{
printf("Could not obtain the IDeckLinkConfiguration interface: %08x\n", (int) result);
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");
}
}
deckLinkConfiguration->Release();
deckLinkConfiguration = NULL;
// Populate the display mode combo with a list of display modes supported by the installed DeckLink card
if (FAILED(s->deckLinkOutput->GetDisplayModeIterator(&displayModeIterator)))
{
fprintf(stderr, "Fatal: cannot create display mode iterator [decklink].\n");
goto error;
}
// pick first display mode, no matter which it is
if(displayModeIterator->Next(&deckLinkDisplayMode) == S_OK)
{
width = deckLinkDisplayMode->GetWidth();
height = deckLinkDisplayMode->GetHeight();
deckLinkDisplayMode->GetFrameRate(&s->frameRateDuration, &s->frameRateScale);
} else {
fprintf(stderr, "[decklink] Fatal: cannot get any display mode.\n");
goto error;
}
s->frames = 0;
s->deckLinkOutput->CreateVideoFrame(width, height, width*2, bmdFormat8BitYUV, bmdFrameFlagDefault, &s->deckLinkFrame);
s->delegate = new PlaybackDelegate(s);
// Provide this class as a delegate to the audio and video output interfaces
s->deckLinkOutput->SetScheduledFrameCompletionCallback(s->delegate);
//s->state[i].deckLinkOutput->DisableAudioOutput();
//
s->deckLinkOutput->EnableVideoOutput(deckLinkDisplayMode->GetDisplayMode(), bmdVideoOutputFlagDefault);
displayModeIterator->Release();
displayModeIterator = NULL;
deckLinkDisplayMode->Release();
deckLinkDisplayMode = NULL;
return (void *)s;
error:
if (displayModeIterator)
displayModeIterator->Release();
if (deckLinkDisplayMode)
deckLinkDisplayMode->Release();
if (deckLinkConfiguration)
deckLinkConfiguration->Release();
if (s->deckLinkOutput != NULL)
s->deckLinkOutput->Release();
if (s->deckLink != NULL)
s->deckLink->Release();
free(s);
return NULL;
}
void decklink_put_frame(void *state, struct audio_frame *frame)
{
struct state_decklink *s = (struct state_decklink *)state;
unsigned int sampleFrameCount = frame->data_len /
(s->audio_desc.bps * s->audio_desc.ch_count);
#ifdef WIN32
unsigned long int sampleFramesWritten;
#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 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 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(&tmp_frame,
s->output_audio_channel_count);
data = tmp_frame.data;
}
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,
int sample_rate) {
struct state_decklink *s = (struct state_decklink *)state;
BMDAudioSampleType sample_type;
s->audio_desc.bps = quant_samples / 8;
s->audio_desc.sample_rate = sample_rate;
s->output_audio_channel_count = s->audio_desc.ch_count = channels;
if (s->audio_desc.ch_count != 1 &&
s->audio_desc.ch_count != 2 &&
s->audio_desc.ch_count != 8 &&
s->audio_desc.ch_count != 16) {
fprintf(stderr, "[decklink] requested channel count isn't supported: "
"%d\n", s->audio_desc.ch_count);
return FALSE;
}
/* toggle one channel to supported two */
if(s->audio_desc.ch_count == 1) {
s->output_audio_channel_count = 2;
}
if((quant_samples != 16 && quant_samples != 32) ||
sample_rate != 48000) {
fprintf(stderr, "[decklink] audio format isn't supported: "
"samples: %d, sample rate: %d\n",
quant_samples, sample_rate);
return FALSE;
}
switch(quant_samples) {
case 16:
sample_type = bmdAudioSampleType16bitInteger;
break;
case 32:
sample_type = bmdAudioSampleType32bitInteger;
break;
default:
return FALSE;
}
s->deckLinkOutput->EnableAudioOutput(bmdAudioSampleRate48kHz,
sample_type,
s->output_audio_channel_count,
bmdAudioOutputStreamContinuous);
s->deckLinkOutput->StartScheduledPlayback(0, s->frameRateScale, s->frameRateDuration);
return TRUE;
}
void decklink_close_playback(void *state)
{
struct state_decklink *s = (struct state_decklink *)state;
s->deckLinkOutput->DisableAudioOutput();
s->deckLinkOutput->DisableVideoOutput();
s->deckLinkFrame->Release();
s->deckLink->Release();
s->deckLinkOutput->Release();
free(s);
}