[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