[Sumover-dev] [svn commit] r4560 - vic/branches/mpeg4/video
sumover-dev at cs.ucl.ac.uk
sumover-dev at cs.ucl.ac.uk
Mon Jan 11 10:52:49 GMT 2010
Author: douglask
Date: Mon Jan 11 10:52:49 2010
New Revision: 4560
Added:
vic/branches/mpeg4/video/grabber-decklink.cpp
Log:
Initial check-in for linux version of grabber for Blackmagic Design's DeckLink,
Multibridge and Intensity products.
TODO:
o Add configure script support
o MacOS X port
o Windows port
o Use ffmpeg's swscale to scale frames down for medium and small captures
o Use ffmpeg's swscale to convert HDYC (a packed UYVY variation) to planar YUYV
o Optimise producer/consumer buffer somehow to increase fps
producer: DeckLinkCaptureDelegate:VideoInputFrameArrived()
consumer: DeckLinkGrabber::grab()
Added: vic/branches/mpeg4/video/grabber-decklink.cpp
==============================================================================
--- (empty file)
+++ vic/branches/mpeg4/video/grabber-decklink.cpp Mon Jan 11 10:52:49 2010
@@ -0,0 +1,624 @@
+
+/*
+ * grabber-declink.cpp
+ *
+ * Copyright (c) 2009 The University of Queensland.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are 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 names of the copyright holders 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 COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
+ * IS'' AND ANY EXPRESS 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 REGENTS 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.
+ *
+ *
+ * Written by Douglas Kosovic <d.kosovic at uq.edu.au>
+ *
+ * VIC Grabber for the Blackmagic Design DeckLink SDK
+ * Supports Blackmagic Design's DeckLink, Multibridge and Intensity products.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "grabber.h"
+#include "module.h"
+#include "device-input.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "DeckLinkAPI.h"
+#include "yuv_convert.h"
+
+#undef debug_msg
+#define debug_msg(args...) fprintf(stderr, args)
+
+
+#define NUM_DEVS 5 // max number of DeckLink capture devices we'll support
+
+// Color subsampling options.
+#define CF_422 0
+#define CF_420 1
+#define CF_CIF 2
+
+// CIF resolution
+#define CIF_WIDTH 352
+#define CIF_HEIGHT 288
+
+// QCIF resolution
+#define QCIF_WIDTH 176
+#define QCIF_HEIGHT 144
+
+class DeckLinkCaptureDelegate : public IDeckLinkInputCallback {
+public:
+ DeckLinkCaptureDelegate(int32_t width, int32_t height) {
+ mRefCount = 1;
+ mReadIndex = 0;
+ mWriteIndex = 0;
+ for (int i = 0; i < mBufferSize; i++) {
+ mBuffer[i] = new uint8_t[width * height * 2];
+ memset((void *)mBuffer[i], width * height, sizeof(uint8_t));
+ }
+ }
+
+ ~DeckLinkCaptureDelegate() {
+ for (int i = 0; i < mBufferSize; i++) {
+ delete [] mBuffer[i];
+ }
+ }
+
+ uint8_t *GetVideoFrame() {
+
+ if(mReadIndex == mWriteIndex) {
+ return NULL;
+ }
+
+ uint8_t *retval = (uint8_t *)(mBuffer[mReadIndex]);
+ int nextElement = (mReadIndex + 1) % mBufferSize;
+ mReadIndex = nextElement;
+
+// fprintf(stderr, "*pop * mBuffer[%i] = 0x%lx\n", mReadIndex, mBuffer[mReadIndex]);
+
+ return retval;
+
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) {
+ return E_NOINTERFACE;
+ }
+
+ virtual ULONG STDMETHODCALLTYPE AddRef() {
+ return __sync_fetch_and_add(&mRefCount, 1);
+ }
+
+ virtual ULONG STDMETHODCALLTYPE Release() {
+ int32_t newRefValue;
+
+ newRefValue = __sync_fetch_and_sub(&mRefCount, 1);
+
+ if (newRefValue == 0) {
+ delete this;
+ return 0;
+ }
+ return newRefValue;
+ }
+
+ virtual HRESULT VideoInputFrameArrived(IDeckLinkVideoInputFrame* arrivedFrame, IDeckLinkAudioInputPacket*) {
+
+ void *videoFrame;
+ arrivedFrame->GetBytes(&videoFrame);
+
+ int nextElementIndex = (mWriteIndex + 1) % mBufferSize;
+
+ if(nextElementIndex != mReadIndex) {
+ memcpy((void *)(mBuffer[mWriteIndex]), videoFrame, arrivedFrame->GetRowBytes() * arrivedFrame->GetHeight());
+
+// fprintf(stderr, "*push* mBuffer[%i] = 0x%lx\n", mWriteIndex, mBuffer[mWriteIndex]);
+ mWriteIndex = nextElementIndex;
+
+ }
+
+ return S_OK;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged(BMDVideoInputFormatChangedEvents, IDeckLinkDisplayMode*, BMDDetectedVideoInputFormatFlags) {
+ return S_OK;
+ }
+
+
+private:
+ volatile int32_t mRefCount;
+ volatile int32_t mReadIndex;
+ volatile int32_t mWriteIndex;
+ static const int32_t mBufferSize = 4;
+ volatile uint8_t *mBuffer[mBufferSize];
+};
+
+
+class DeckLinkGrabber : public Grabber {
+public:
+ DeckLinkGrabber(const char *cformat, IDeckLink* deckLink);
+ virtual ~DeckLinkGrabber();
+
+ virtual int command(int argc, const char*const* argv);
+ void start();
+ void stop();
+ virtual int grab();
+
+protected:
+ IDeckLink* deckLink_;
+ IDeckLinkInput *deckLinkInput_;
+
+ int cformat_;
+ int decimate_;
+ int width_, height_;
+
+ BMDDisplayMode displayMode_;
+ long displayModeWidth_;
+ long displayModeHeight_;
+ BMDTimeValue displayModeFrameDuration_;
+ BMDTimeScale displayModeTimeScale_;
+
+ DeckLinkCaptureDelegate *delegate_;
+};
+
+class DeckLinkDevice : public InputDevice {
+public:
+ DeckLinkDevice(const char* name, IDeckLink* deckLink);
+ ~DeckLinkDevice();
+ virtual int command(int argc, const char*const* argv);
+protected:
+ DeckLinkGrabber *grabber_;
+ IDeckLink* deckLink_;
+};
+
+class DeckLinkScanner {
+public:
+ DeckLinkScanner(int maxNumDevices);
+ ~DeckLinkScanner();
+protected:
+ DeckLinkDevice *devs_[NUM_DEVS];
+
+};
+
+static DeckLinkScanner find_decklink_devices(NUM_DEVS);
+
+DeckLinkScanner::DeckLinkScanner(int maxNumDevices)
+{
+ IDeckLinkIterator* deckLinkIterator;
+ IDeckLink* deckLink;
+ int n = 0;
+ HRESULT result;
+
+ deckLinkIterator = CreateDeckLinkIteratorInstance();
+ if (deckLinkIterator == NULL) {
+ debug_msg("DeckLinkScanner: DeckLink iterator instance could not be created\n");
+ return;
+ }
+
+ memset(devs_, 0, sizeof(devs_));
+ // Enumerate all DeckLink cards on this system
+ while (deckLinkIterator->Next(&deckLink) == S_OK && n < maxNumDevices) {
+ const char * deviceNameString = NULL;
+
+ result = deckLink->GetModelName(&deviceNameString);
+ if (result == S_OK) {
+ debug_msg("Adding device %s\n", deviceNameString);
+
+
+ char *nick = new char[strlen(deviceNameString) + 10];
+ sprintf(nick,"DeckLink-%s", deviceNameString);
+
+ devs_[n] = new DeckLinkDevice(nick, deckLink);
+ n++;
+ }
+ }
+}
+
+DeckLinkScanner::~DeckLinkScanner() {
+ debug_msg("~DeckLinkScanner\n");
+ /*
+ for (int i = 0; i < NUM_DEVS; i++) {
+ if (devs_[i]) {
+ debug_msg("Deleteing device %d\n", i);
+ delete devs_[i];
+ }
+ }
+ */
+}
+
+DeckLinkDevice::DeckLinkDevice(const char* name, IDeckLink* deckLink) : InputDevice(name), grabber_(0), deckLink_(deckLink)
+{
+ char *attr = new char[512];
+ IDeckLinkConfiguration *deckLinkConfiguration = NULL;
+ IDeckLinkConfiguration *deckLinkValidator = NULL;
+ IDeckLinkInput *deckLinkInput = NULL;
+ IDeckLinkDisplayModeIterator *displayModeIterator = NULL;
+ IDeckLinkDisplayMode *displayMode = NULL;
+
+ HRESULT result;
+
+ result = deckLink->QueryInterface(IID_IDeckLinkConfiguration, (void**)&deckLinkConfiguration);
+ if (result != S_OK) {
+ debug_msg("DecLinkDevice: Could not obtain the IDeckLinkConfiguration interface - %08x\n", result);
+ strcpy(attr,"disabled");
+ return;
+ }
+
+ result = deckLinkConfiguration->GetConfigurationValidator(&deckLinkValidator);
+ if (result != S_OK) {
+ debug_msg("DecLinkDevice: Could not obtain the configuration validator interface\n");
+ strcpy(attr,"disabled");
+ return;
+ }
+
+ strcpy(attr,"format { 420 422 cif } ");
+ strcat(attr,"size { small large cif } ");
+
+ strcat(attr,"port { ");
+
+ if (deckLinkValidator->SetVideoInputFormat(bmdVideoConnectionSDI) == S_OK) {
+ strcat(attr,"SDI ");
+ }
+ if (deckLinkValidator->SetVideoInputFormat(bmdVideoConnectionHDMI) == S_OK) {
+ strcat(attr,"HDMI ");
+ }
+ if (deckLinkValidator->SetVideoInputFormat(bmdVideoConnectionComponent) == S_OK) {
+ strcat(attr,"Component ");
+ }
+ if (deckLinkValidator->SetVideoInputFormat(bmdVideoConnectionComposite) == S_OK) {
+ strcat(attr,"Composite ");
+ }
+ if (deckLinkValidator->SetVideoInputFormat(bmdVideoConnectionSVideo) == S_OK) {
+ strcat(attr,"S-Video ");
+ }
+ if (deckLinkValidator->SetVideoInputFormat(bmdVideoConnectionOpticalSDI) == S_OK) {
+ strcat(attr,"Optical-SDI ");
+ }
+ strcat(attr,"} ");
+
+ result = deckLink->QueryInterface(IID_IDeckLinkInput, (void**)&deckLinkInput);
+ if (result != S_OK) {
+ debug_msg("DecLinkDevice: Could not obtain the IDeckLinkInput interface\n");
+ strcpy(attr, "disabled");
+ return;
+ }
+
+ result = deckLinkInput->GetDisplayModeIterator(&displayModeIterator);
+ if (result != S_OK) {
+ debug_msg("DecLinkDevice: Could not obtain the video input display mode iterator\n");
+ strcpy(attr, "disabled");
+ return;
+ }
+
+ strcat(attr,"type { ");
+ while (displayModeIterator->Next(&displayMode) == S_OK) {
+ const char *displayModeString = NULL;
+ char typeString[128];
+
+ result = displayMode->GetName(&displayModeString);
+ if (result == S_OK) {
+ strncpy(typeString, displayModeString, sizeof(typeString));
+ typeString[sizeof(typeString) - 1] = 0;
+ for (unsigned int i=0 ; i < strlen(typeString) ; i++) {
+ if (typeString[i]==' ') typeString[i]='-';
+ }
+
+ strcat(attr, typeString);
+ strcat(attr, " ");
+ }
+ displayMode->Release();
+ }
+ strcat(attr,"} ");
+
+ attributes_ = attr;
+ debug_msg("DeckLinkDevice: ==> %s\n",attr);
+
+
+ if (displayModeIterator != NULL) {
+ displayModeIterator->Release();
+ }
+
+ if (deckLinkInput != NULL) {
+ deckLinkInput->Release();
+ }
+
+ if (deckLinkValidator != NULL) {
+ deckLinkValidator->Release();
+ }
+
+ if (deckLinkConfiguration != NULL) {
+ deckLinkConfiguration->Release();
+ }
+}
+
+int DeckLinkDevice::command(int argc, const char*const* argv)
+{
+ Tcl& tcl = Tcl::instance();
+ if (argc == 3) {
+ if (strcmp(argv[1], "open") == 0) {
+ TclObject* o = 0;
+ o = new DeckLinkGrabber(argv[2], deckLink_);
+ if (o != 0)
+ tcl.result(o->name());
+ return (TCL_OK);
+ }
+ }
+ return (InputDevice::command(argc, argv));
+}
+
+DeckLinkDevice::~DeckLinkDevice()
+{
+}
+
+
+
+DeckLinkGrabber::DeckLinkGrabber(const char *cformat, IDeckLink* deckLink) :
+ deckLink_(deckLink), displayMode_(NULL)
+{
+ HRESULT result;
+
+ if (strcmp(cformat, "422") == 0) cformat_ = CF_422;
+ else if (strcmp(cformat, "420") == 0) cformat_ = CF_420;
+ else if (strcmp(cformat, "cif") == 0) cformat_ = CF_CIF;
+ else cformat_ = CF_420;
+
+ result = deckLink->QueryInterface(IID_IDeckLinkInput, (void**)&deckLinkInput_);
+ if (result != S_OK) {
+ debug_msg("DecLinkDevice: Could not obtain the IDeckLinkInput interface\n");
+ }
+ running_ = 0;
+}
+
+int DeckLinkGrabber::command(int argc, const char*const* argv)
+{
+ HRESULT result;
+
+ if (argc == 3) {
+ if (strcmp(argv[1], "decimate") == 0) {
+ decimate_ = atoi(argv[2]);
+
+ if (running_) {
+ stop(); start();
+ }
+ return (TCL_OK);
+ }
+
+ if (strcmp(argv[1], "port") == 0) {
+ IDeckLinkConfiguration *deckLinkConfiguration = NULL;
+ BMDVideoConnection bmdVideoConnection = bmdVideoConnectionHDMI;
+
+ result = deckLink_->QueryInterface(IID_IDeckLinkConfiguration, (void**)&deckLinkConfiguration);
+
+ if (result != S_OK) {
+ debug_msg("DecLinkGrabber: Could not obtain the IDeckLinkConfiguration interface\n");
+ return TCL_ERROR;
+ }
+
+ if (strcasecmp(argv[2], "SDI") == 0) {
+ bmdVideoConnection = bmdVideoConnectionSDI;
+
+ } else if(strcasecmp(argv[2], "HDMI") == 0) {
+ bmdVideoConnection = bmdVideoConnectionHDMI;
+
+ } else if(strcasecmp(argv[2], "Component") == 0) {
+ bmdVideoConnection = bmdVideoConnectionComponent;
+
+ } else if(strcasecmp(argv[2], "Composite") == 0) {
+ bmdVideoConnection = bmdVideoConnectionComposite;
+
+ } else if(strcasecmp(argv[2], "S-Video") == 0) {
+ bmdVideoConnection = bmdVideoConnectionSVideo;
+
+ } else if(strcasecmp(argv[2], "Optical-SDI") == 0) {
+ bmdVideoConnection = bmdVideoConnectionOpticalSDI;
+ }
+
+ result = deckLinkConfiguration->SetVideoInputFormat(bmdVideoConnection);
+
+ if (result != S_OK) {
+ debug_msg("DecLinkGrabber: Could not set input video connection\n");
+ return TCL_ERROR;
+ }
+
+ if (deckLinkConfiguration != NULL) {
+ deckLinkConfiguration->Release();
+ }
+
+ if (running_) {
+ stop(); start();
+ }
+
+ return (TCL_OK);
+ }
+
+ if (strcmp(argv[1], "fps") == 0) {
+ debug_msg("V4L2: fps %s\n",argv[2]);
+ }
+
+ if (strcmp(argv[1], "type") == 0 || strcmp(argv[1], "format") == 0) {
+
+ IDeckLinkDisplayModeIterator *displayModeIterator = NULL;
+ IDeckLinkDisplayMode *displayMode = NULL;
+
+ result = deckLinkInput_->GetDisplayModeIterator(&displayModeIterator);
+ if (result != S_OK) {
+ debug_msg("DecLinkDevice: Could not obtain the video input display mode iterator\n");
+ return TCL_ERROR;
+ }
+
+ while (displayModeIterator->Next(&displayMode) == S_OK) {
+ const char *displayModeString = NULL;
+ char typeString[128];
+
+ result = displayMode->GetName(&displayModeString);
+ if (result == S_OK) {
+ strncpy(typeString, displayModeString, sizeof(typeString));
+ typeString[sizeof(typeString) - 1] = 0;
+ for (unsigned int i=0 ; i < strlen(typeString) ; i++) {
+ if (typeString[i]==' ') typeString[i] = '-';
+ }
+ if (strcasecmp(argv[2], typeString) == 0) {
+ displayMode_ = displayMode->GetDisplayMode();
+ displayModeWidth_ = displayMode->GetWidth();
+ displayModeHeight_ = displayMode->GetHeight();
+ displayMode->GetFrameRate(&displayModeFrameDuration_, &displayModeTimeScale_);
+fprintf(stderr, "DisplayMode width=%li height=%li frame duration=%li time scale=%li\n", displayModeWidth_, displayModeHeight_,displayModeFrameDuration_, displayModeTimeScale_);
+ break;
+ }
+ }
+ }
+
+ if (displayMode != NULL) {
+ displayMode->Release();
+ }
+
+ if (displayModeIterator != NULL) {
+ displayModeIterator->Release();
+ }
+
+ if (running_) {
+ stop(); start();
+ }
+
+ return TCL_OK;
+ }
+
+ } else if (argc == 2) {
+ if (strcmp(argv[1], "format") == 0 ||
+ strcmp(argv[1], "type") == 0) {
+ return TCL_OK;
+ }
+ }
+
+ return (Grabber::command(argc, argv));
+
+}
+
+DeckLinkGrabber::~DeckLinkGrabber()
+{
+
+ if (deckLinkInput_ != NULL) {
+ deckLinkInput_->Release();
+ }
+}
+
+void DeckLinkGrabber::start()
+{
+ HRESULT result;
+
+ // Set the image size.
+ switch (decimate_) {
+ case 1: // full-sized
+ width_ = displayModeWidth_;
+ height_ = displayModeHeight_;
+ break;
+ case 2: // CIF-sized
+ width_ = CIF_WIDTH;
+ height_ = CIF_HEIGHT;
+ break;
+ case 4: // QCIF-sized
+ width_ = QCIF_WIDTH;
+ height_ = QCIF_HEIGHT;
+ break;
+ }
+
+ switch (cformat_) {
+ case CF_422:
+ set_size_422(width_, height_);
+ break;
+ case CF_420:
+ set_size_420(width_, height_);
+ break;
+ case CF_CIF:
+ set_size_cif(width_, height_);
+ break;
+ }
+
+ result = deckLinkInput_->EnableVideoInput(displayMode_, bmdFormat8BitYUV, 0);
+ if (result != S_OK) {
+ debug_msg("DecLinkGrabber: Could not enable video input\n");
+ return;
+ }
+
+ delegate_ = new DeckLinkCaptureDelegate(width_, height_);
+
+ result = deckLinkInput_->SetCallback(delegate_);
+
+ if (result != S_OK) {
+ debug_msg("DecStartStreams();LinkGrabber: Could not set callback\n");
+ return;
+ }
+
+ result = deckLinkInput_->StartStreams();
+
+ if (result != S_OK) {
+ debug_msg("DecStartStreams();LinkGrabber: Could not start streams\n");
+ return;
+ }
+
+ // Allocate the reference buffer.
+ allocref();
+
+ fprintf(stderr, "Grabber::start()\n");
+ Grabber::start();
+ running_ = 1;
+}
+
+void DeckLinkGrabber::stop()
+{
+ deckLinkInput_->StopStreams();
+
+ Grabber::stop();
+ running_ = 0;
+}
+
+int DeckLinkGrabber::grab()
+{
+ if (!delegate_) {
+ return 0;
+ }
+
+ uint8_t *fr = delegate_->GetVideoFrame();
+ if (fr == NULL) {
+ return 0;
+ }
+
+ // Need to fix, should do HDYC (a packed UYVY variation) to YUYV instead
+ switch (cformat_) {
+ case CF_420:
+ case CF_CIF:
+ packedUYVY422_to_planarYUYV420((char *)frame_, outw_, outh_, (char *)fr, inw_, inh_);
+ break;
+
+ case CF_422:
+ packedUYVY422_to_planarYUYV422((char *)frame_, outw_, outh_, (char *)fr, inw_, inh_);
+ break;
+ }
+
+ suppress(frame_);
+ saveblks(frame_);
+ YuvFrame f(media_ts(), frame_, crvec_, outw_, outh_);
+ return (target_->consume(&f));
+
+}
More information about the Sumover-dev
mailing list