gstreamer/sys/decklink/capture.cpp
David Schleef 8961f6a900 decklink: Add decklink plugin
Source and sink elements for BlackMagic DeckLink SDI cards.
2011-03-23 21:33:49 -07:00

430 lines
12 KiB
C++

/* -LICENSE-START-
** Copyright (c) 2009 Blackmagic Design
**
** Permission is hereby granted, free of charge, to any person or organization
** obtaining a copy of the software and accompanying documentation covered by
** this license (the "Software") to use, reproduce, display, distribute,
** execute, and transmit the Software, and to prepare derivative works of the
** Software, and to permit third-parties to whom the Software is furnished to
** do so, all subject to the following:
**
** The copyright notices in the Software and this entire statement, including
** the above license grant, this restriction and the following disclaimer,
** must be included in all copies of the Software, in whole or in part, and
** all derivative works of the Software, unless such copies or derivative
** works are solely in the form of machine-executable object code generated by
** a source language processor.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
** DEALINGS IN THE SOFTWARE.
** -LICENSE-END-
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include "gstdecklinksrc.h"
#include "DeckLinkAPI.h"
#include "capture.h"
int videoOutputFile = -1;
int audioOutputFile = -1;
IDeckLink *deckLink;
IDeckLinkInput *deckLinkInput;
IDeckLinkDisplayModeIterator *displayModeIterator;
static BMDTimecodeFormat g_timecodeFormat = 0;
DeckLinkCaptureDelegate::DeckLinkCaptureDelegate ():m_refCount (0)
{
pthread_mutex_init (&m_mutex, NULL);
}
DeckLinkCaptureDelegate::~DeckLinkCaptureDelegate ()
{
pthread_mutex_destroy (&m_mutex);
}
ULONG
DeckLinkCaptureDelegate::AddRef (void)
{
pthread_mutex_lock (&m_mutex);
m_refCount++;
pthread_mutex_unlock (&m_mutex);
return (ULONG) m_refCount;
}
ULONG
DeckLinkCaptureDelegate::Release (void)
{
pthread_mutex_lock (&m_mutex);
m_refCount--;
pthread_mutex_unlock (&m_mutex);
if (m_refCount == 0) {
delete this;
return 0;
}
return (ULONG) m_refCount;
}
HRESULT
DeckLinkCaptureDelegate::VideoInputFrameArrived (IDeckLinkVideoInputFrame *
videoFrame, IDeckLinkAudioInputPacket * audioFrame)
{
GstDecklinkSrc *decklinksrc = GST_DECKLINK_SRC (priv);
// Handle Video Frame
if (videoFrame) {
if (videoFrame->GetFlags () & bmdFrameHasNoInputSource) {
GST_DEBUG("Frame received - No input signal detected");
} else {
const char *timecodeString = NULL;
if (g_timecodeFormat != 0) {
IDeckLinkTimecode *timecode;
if (videoFrame->GetTimecode (g_timecodeFormat, &timecode) == S_OK) {
timecode->GetString (&timecodeString);
}
}
GST_DEBUG("Frame received [%s] - %s - Size: %li bytes",
timecodeString != NULL ? timecodeString : "No timecode",
"Valid Frame",
videoFrame->GetRowBytes () * videoFrame->GetHeight ());
if (timecodeString)
free ((void *) timecodeString);
g_mutex_lock (decklinksrc->mutex);
if (decklinksrc->video_frame != NULL) {
decklinksrc->dropped_frames++;
} else {
videoFrame->AddRef();
decklinksrc->video_frame = videoFrame;
if (audioFrame) {
audioFrame->AddRef();
decklinksrc->audio_frame = audioFrame;
}
}
g_cond_signal (decklinksrc->cond);
g_mutex_unlock (decklinksrc->mutex);
}
}
return S_OK;
}
HRESULT
DeckLinkCaptureDelegate::
VideoInputFormatChanged (BMDVideoInputFormatChangedEvents events,
IDeckLinkDisplayMode * mode, BMDDetectedVideoInputFormatFlags)
{
GST_ERROR("moo");
return S_OK;
}
#ifdef unused
int
usage (int status)
{
HRESULT result;
IDeckLinkDisplayMode *displayMode;
int displayModeCount = 0;
fprintf (stderr,
"Usage: Capture -m <mode id> [OPTIONS]\n" "\n" " -m <mode id>:\n");
while (displayModeIterator->Next (&displayMode) == S_OK) {
char *displayModeString = NULL;
result = displayMode->GetName ((const char **) &displayModeString);
if (result == S_OK) {
BMDTimeValue frameRateDuration, frameRateScale;
displayMode->GetFrameRate (&frameRateDuration, &frameRateScale);
fprintf (stderr, " %2d: %-20s \t %li x %li \t %g FPS\n",
displayModeCount, displayModeString, displayMode->GetWidth (),
displayMode->GetHeight (),
(double) frameRateScale / (double) frameRateDuration);
free (displayModeString);
displayModeCount++;
}
// Release the IDeckLinkDisplayMode object to prevent a leak
displayMode->Release ();
}
fprintf (stderr,
" -p <pixelformat>\n"
" 0: 8 bit YUV (4:2:2) (default)\n"
" 1: 10 bit YUV (4:2:2)\n"
" 2: 10 bit RGB (4:4:4)\n"
" -t <format> Print timecode\n"
" rp188: RP 188\n"
" vitc: VITC\n"
" serial: Serial Timecode\n"
" -f <filename> Filename raw video will be written to\n"
" -a <filename> Filename raw audio will be written to\n"
" -c <channels> Audio Channels (2, 8 or 16 - default is 2)\n"
" -s <depth> Audio Sample Depth (16 or 32 - default is 16)\n"
" -n <frames> Number of frames to capture (default is unlimited)\n"
" -3 Capture Stereoscopic 3D (Requires 3D Hardware support)\n"
"\n"
"Capture video and/or audio to a file. Raw video and/or audio can be viewed with mplayer eg:\n"
"\n"
" Capture -m2 -n 50 -f video.raw -a audio.raw\n"
" mplayer video.raw -demuxer rawvideo -rawvideo pal:uyvy -audiofile audio.raw -audio-demuxer 20 -rawaudio rate=48000\n");
exit (status);
}
int
main (int argc, char *argv[])
{
IDeckLinkIterator *deckLinkIterator = CreateDeckLinkIteratorInstance ();
DeckLinkCaptureDelegate *delegate;
IDeckLinkDisplayMode *displayMode;
BMDVideoInputFlags inputFlags = 0;
BMDDisplayMode selectedDisplayMode = bmdModeNTSC;
BMDPixelFormat pixelFormat = bmdFormat8BitYUV;
int displayModeCount = 0;
int exitStatus = 1;
int ch;
bool foundDisplayMode = false;
HRESULT result;
pthread_mutex_init (&sleepMutex, NULL);
pthread_cond_init (&sleepCond, NULL);
if (!deckLinkIterator) {
fprintf (stderr,
"This application requires the DeckLink drivers installed.\n");
goto bail;
}
/* Connect to the first DeckLink instance */
result = deckLinkIterator->Next (&deckLink);
if (result != S_OK) {
fprintf (stderr, "No DeckLink PCI cards found.\n");
goto bail;
}
if (deckLink->QueryInterface (IID_IDeckLinkInput,
(void **) &deckLinkInput) != S_OK)
goto bail;
delegate = new DeckLinkCaptureDelegate ();
deckLinkInput->SetCallback (delegate);
// Obtain an IDeckLinkDisplayModeIterator to enumerate the display modes supported on output
result = deckLinkInput->GetDisplayModeIterator (&displayModeIterator);
if (result != S_OK) {
fprintf (stderr,
"Could not obtain the video output display mode iterator - result = %08x\n",
result);
goto bail;
}
// Parse command line options
while ((ch = getopt (argc, argv, "?h3c:s:f:a:m:n:p:t:")) != -1) {
switch (ch) {
case 'm':
g_videoModeIndex = atoi (optarg);
break;
case 'c':
g_audioChannels = atoi (optarg);
if (g_audioChannels != 2 &&
g_audioChannels != 8 && g_audioChannels != 16) {
fprintf (stderr,
"Invalid argument: Audio Channels must be either 2, 8 or 16\n");
goto bail;
}
break;
case 's':
g_audioSampleDepth = atoi (optarg);
if (g_audioSampleDepth != 16 && g_audioSampleDepth != 32) {
fprintf (stderr,
"Invalid argument: Audio Sample Depth must be either 16 bits or 32 bits\n");
goto bail;
}
break;
case 'f':
g_videoOutputFile = optarg;
break;
case 'a':
g_audioOutputFile = optarg;
break;
case 'n':
g_maxFrames = atoi (optarg);
break;
case '3':
inputFlags |= bmdVideoInputDualStream3D;
break;
case 'p':
switch (atoi (optarg)) {
case 0:
pixelFormat = bmdFormat8BitYUV;
break;
case 1:
pixelFormat = bmdFormat10BitYUV;
break;
case 2:
pixelFormat = bmdFormat10BitRGB;
break;
default:
fprintf (stderr, "Invalid argument: Pixel format %d is not valid",
atoi (optarg));
goto bail;
}
break;
case 't':
if (!strcmp (optarg, "rp188"))
g_timecodeFormat = bmdTimecodeRP188;
else if (!strcmp (optarg, "vitc"))
g_timecodeFormat = bmdTimecodeVITC;
else if (!strcmp (optarg, "serial"))
g_timecodeFormat = bmdTimecodeSerial;
else {
fprintf (stderr,
"Invalid argument: Timecode format \"%s\" is invalid\n", optarg);
goto bail;
}
break;
case '?':
case 'h':
usage (0);
}
}
if (g_videoModeIndex < 0) {
fprintf (stderr, "No video mode specified\n");
usage (0);
}
if (g_videoOutputFile != NULL) {
videoOutputFile =
open (g_videoOutputFile, O_WRONLY | O_CREAT | O_TRUNC, 0664);
if (videoOutputFile < 0) {
fprintf (stderr, "Could not open video output file \"%s\"\n",
g_videoOutputFile);
goto bail;
}
}
if (g_audioOutputFile != NULL) {
audioOutputFile =
open (g_audioOutputFile, O_WRONLY | O_CREAT | O_TRUNC, 0664);
if (audioOutputFile < 0) {
fprintf (stderr, "Could not open audio output file \"%s\"\n",
g_audioOutputFile);
goto bail;
}
}
while (displayModeIterator->Next (&displayMode) == S_OK) {
if (g_videoModeIndex == displayModeCount) {
BMDDisplayModeSupport result;
const char *displayModeName;
foundDisplayMode = true;
displayMode->GetName (&displayModeName);
selectedDisplayMode = displayMode->GetDisplayMode ();
deckLinkInput->DoesSupportVideoMode (selectedDisplayMode, pixelFormat,
bmdVideoInputFlagDefault, &result, NULL);
if (result == bmdDisplayModeNotSupported) {
fprintf (stderr,
"The display mode %s is not supported with the selected pixel format\n",
displayModeName);
goto bail;
}
if (inputFlags & bmdVideoInputDualStream3D) {
if (!(displayMode->GetFlags () & bmdDisplayModeSupports3D)) {
fprintf (stderr, "The display mode %s is not supported with 3D\n",
displayModeName);
goto bail;
}
}
break;
}
displayModeCount++;
displayMode->Release ();
}
if (!foundDisplayMode) {
fprintf (stderr, "Invalid mode %d specified\n", g_videoModeIndex);
goto bail;
}
result =
deckLinkInput->EnableVideoInput (selectedDisplayMode, pixelFormat,
inputFlags);
if (result != S_OK) {
fprintf (stderr,
"Failed to enable video input. Is another application using the card?\n");
goto bail;
}
result =
deckLinkInput->EnableAudioInput (bmdAudioSampleRate48kHz,
g_audioSampleDepth, g_audioChannels);
if (result != S_OK) {
goto bail;
}
result = deckLinkInput->StartStreams ();
if (result != S_OK) {
goto bail;
}
// All Okay.
exitStatus = 0;
// Block main thread until signal occurs
pthread_mutex_lock (&sleepMutex);
pthread_cond_wait (&sleepCond, &sleepMutex);
pthread_mutex_unlock (&sleepMutex);
fprintf (stderr, "Stopping Capture\n");
bail:
if (videoOutputFile)
close (videoOutputFile);
if (audioOutputFile)
close (audioOutputFile);
if (displayModeIterator != NULL) {
displayModeIterator->Release ();
displayModeIterator = NULL;
}
if (deckLinkInput != NULL) {
deckLinkInput->Release ();
deckLinkInput = NULL;
}
if (deckLink != NULL) {
deckLink->Release ();
deckLink = NULL;
}
if (deckLinkIterator != NULL)
deckLinkIterator->Release ();
return exitStatus;
}
#endif