/* -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 #include #include #include #include #include #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 [OPTIONS]\n" "\n" " -m :\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 \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 Print timecode\n" " rp188: RP 188\n" " vitc: VITC\n" " serial: Serial Timecode\n" " -f Filename raw video will be written to\n" " -a Filename raw audio will be written to\n" " -c Audio Channels (2, 8 or 16 - default is 2)\n" " -s Audio Sample Depth (16 or 32 - default is 16)\n" " -n 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