Menu

Interlaced Rendering - it's possible?

Help
2016-07-22
2016-08-25
  • Rick Newman

    Rick Newman - 2016-07-22

    Is there any way to render Interlaced (for tv purpose: 1080i)?

    Actual Solution: I'm rendering my scene every 20msecs and copying odds and even rows to create 1 frame every 40msecs. This "interlaced" frame goes to a Graphic Engine.

    The problem with this is that tooks a lot of copyes and scanlines to do this.
    We are making a little scene editor but the final result must go to a TV OUT in an Interlaced Mode.

    PD: searching on Internet I read this:
    *There's no reason to render 576 lines then discard half of them; you can just render 288 lines for each field.

    One slightly tricky point is to get the projection matrix correct. The pixel centres for line i (0<=i<288) should be at (2i+0.5)/576 for an even[] field or (2i+1.5)/576 for an odd[] field. To achieve this, a projection which is correct for a 576-line frame should be pre-multiplied by a Y-translation of +/- 1/1152 for a 288-line field.
    *

    Anyone knows the way to do this or where?

    Thanks in advance.

     
  • Massimo Zanoletti

    Hi Rick
    I also develop for tv.
    From my experience, the suggestion to render at "half height" is not applicable.
    Or better. It depends on how the graphics (tv) engine you are using receives graphics data.
    If (as usual) it receives a single interlaced (merged) frame (every 40 ms) in any case you have to provide the "merging" job.
    Otherwise, if your grphics tv engine can receive 2 different fields (and then it merges them by itself), in this case you could provide directly 2 diffentent fields, full frame or half frame: it depends on engine requirements.
    This second approach can avoid you to not to do the merging process, but you must be sure that your rendering tv engine works in this way.
    Rendering at half height, for my experience, is not so much faster instead of full frame renderinf (I'm speaking just about OpenGL rendering process, not merging). So half rendering could impact in performances "only" if your rendering tv engine receives 2 separated fields of half height. Only in this case.
    In any case you have to render 2 fields (full of half). And timings to render full or half are not so different with current hardware.
    The bottleneck is the merging process, and you can avoid it only in the case I wrote before.
    Also, rendering at half height involves some complexity about scene viewing.

    If merging is needed (as usual), suggestion is to implement it directly in assembly or using CUDA.

    My approach is like this: OpenGL renders 2 FULL fields in 40 ms (they are time separated by 20 ms, obviously). Then I provide these 2 full images (every 40 ms) to a sw layer (a dll developed by us) that merges them (even and odd lines) and than sends to the graphics tv engine driver.
    This approach is useful (and the same, not only useful) also for "progressive", because OpenGL renders in any case full frames.

    Massimo

     
  • Rick Newman

    Rick Newman - 2016-07-27

    Hi Massimo, what you explain it's exactly what we are doing right now:

    I'm rendering my scene every 20msecs and copying odds(Frame A) and even(Frame B) rows to create 1 Interlaced frame every 40msecs.This frame goes to the Graphic Engine (the engine needs a complete frame).

    What we think to do is, in a Direct OPENGL way, only draw Odds or Even pixels (filling the between pixel lines with alpha) and then blend/merge the FRAME A and FRAME B. This will make a huge performance difference because this way we dont have to scan the Complete Frame (evens and odds pixels) to modify it.

     
  • Massimo Zanoletti

    If tv engine needs a full (interlaced) frame, you have to do the merge operation in any case, even if you render only odd and then even lines for 2 fields. So, you haven't any time saving using a "half height" rendering.
    I know that the merging is the operation that takes huge amount of resources (time and cpu charge).
    For this reason my advice is to implement it in a separate thread directly in assembly language, or (better) to implement it usign CUDA (Cuda is perfect for these kind of operations).

    Some years ago we tried to experiment the "half height" way, but problems were greater than benefits.
    About ten years ago, in tv graphics systems it was quite common to see the half height rendering approach, but the reason was because of OpenGL rendering perfomances, not to avoid merging.
    In recent years, OpenGL cards are fast enough to ensure rendering of 2 full frames, also in HD, also in NTSC frame rate (2 frames into 30 ms). For this reason, no tv graphics system no longer uses the "half height" rendering.

     
  • Rick Newman

    Rick Newman - 2016-07-28

    Hi Massimo, I will investigate the "CUDA" way to do it.

    I really appreciate your help, thanks a lot!

    Regards.

     
  • Torud

    Torud - 2016-08-25

    Hello,
    maybe it is also possible to pass the Framebuffer directly to the Videocard? This is how we did it by using a Decklink-Card. We only have the Problem, that we are not getting a Key out of the card. So we are not sure, if the rendering is really happen with Alpha in Framebuffer.

    So if you are unsing a Decklink card, I could offer some codes.

    Cheers Tom

     
  • Rick Newman

    Rick Newman - 2016-08-25

    Hi Torud, we are not using Decklink cards, but maybe I can "translate" the framebuffer transportation to our SDK, that would be awesome.

    Regards!

     
  • Torud

    Torud - 2016-08-26

    Hi Rick,

    this is all we have. If you or other guys have some expierience of how to render wit alpha to framebuffer by using GLScene - it would be amazing if you would also sharing this. ;-)

    unit MainForm;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
      System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
      DeckLinkAPI_TLB_10_3_1, Winapi.Activex, Vcl.StdCtrls,
      Vcl.ExtCtrls, Vcl.ImgList, Vcl.ComCtrls, GLSimpleNavigation,
      GLScene, GLObjects, GLCoordinates, GLBaseClasses, GLCadencer, GLCrossPlatform,
      GLMaterial, GLWin32Viewer, GLUtils, GLGraphics, GLContext, OpenGLTokens,
      GLAsyncTimer, GLFullScreenViewer, GLFireFX;
    
    type
    
      TCallBackProc = class(TInterfacedObject, IDeckLinkVideoOutputCallback)
      protected
        m_refCount    : integer;
      public
        function ScheduledFrameCompleted(const completedFrame: IDeckLinkVideoFrame;
                                         res: _BMDOutputFrameCompletionResult): HResult; stdcall;
        function ScheduledPlaybackHasStopped: HResult; stdcall;
        function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
        function _AddRef: Integer; stdcall;
        function _Release: Integer; stdcall;
      end;
    
      TFormMain = class(TForm)
        PanelPrev: TPanel;
        MemoLog: TMemo;
        RBKeyingOff: TRadioButton;
        RBInternalKeying: TRadioButton;
        RBExternalKeying: TRadioButton;
        StatusBarMain: TStatusBar;
        CheckBoxShowPrev: TCheckBox;
        CheckBoxDisplayVideoFrameSync: TCheckBox;
        CheckBoxExtendedLogging: TCheckBox;
        GLMaterialLibrary1: TGLMaterialLibrary;
        GLCadencer1: TGLCadencer;
        GLScene1: TGLScene;
        GLCamera1: TGLCamera;
        Sprite1: TGLSprite;
        DummyCube1: TGLDummyCube;
        Sprite2: TGLSprite;
        ScrollBox1: TScrollBox;
        GLSceneViewer1: TGLSceneViewer;
        GLMemoryViewer1: TGLMemoryViewer;
        CheckBoxRenderFromMemory: TCheckBox;
        Timer1: TTimer;
        procedure FormCreate(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
        procedure RBKeyingOffClick(Sender: TObject);
        procedure RBInternalKeyingClick(Sender: TObject);
        procedure RBExternalKeyingClick(Sender: TObject);
        procedure CheckBoxDisplayVideoFrameSyncClick(Sender: TObject);
        procedure CheckBoxExtendedLoggingClick(Sender: TObject);
        procedure GLCadencer1Progress(Sender: TObject; const deltaTime,
          newTime: Double);
        procedure GLAsyncTimer1Timer(Sender: TObject);
        procedure GLSceneViewer1AfterRender(Sender: TObject);
        procedure GLMemoryViewer1AfterRender(Sender: TObject);
        procedure CheckBoxRenderFromMemoryClick(Sender: TObject);
        procedure GLMemoryViewer1PostRender(Sender: TObject);
        procedure CheckBoxShowPrevClick(Sender: TObject);
        procedure Timer1Timer(Sender: TObject);
      private
        m_deckLinkOutput: IDeckLinkOutput;
        m_videoFrameGDI: IDeckLinkMutableVideoFrame;
        m_DeckLinkKeyer: IDeckLinkKeyer;
        m_frameWidth, m_frameHeight: Integer;
        m_frameDuration, m_TimeScale, m_TotalFrames: Int64;
        m_UseDisplayVideoFrameSync: Boolean;
        m_ScheduledCallback: TCallBackProc;
        DoExtendedLogging: Boolean;
        DoRenderFromMemory: Boolean;
        procedure SetupDecklink(DeckLink: IDeckLink);
        procedure AddToLog(Msg: string);
      end;
    
    var
      FormMain: TFormMain;
    
    const
      NearClipping = 1;
      FarClipping  = 1000;
    
    implementation
    
    {$R *.dfm}
    
    uses
      System.Math;
    
    procedure TFormMain.AddToLog(Msg: string);
    begin
      MemoLog.Lines.Add(FormatDateTime('dd.mm.yyyy hh:nn:ss', now) + ': ' + Msg);
      SendMessage(MemoLog.Handle, EM_LINESCROLL, 0, MemoLog.Lines.Count);
    end;
    
    procedure TFormMain.CheckBoxDisplayVideoFrameSyncClick(Sender: TObject);
    var
      StopTime: Int64;
    begin
      m_UseDisplayVideoFrameSync := CheckBoxDisplayVideoFrameSync.Checked;
    
      if not m_UseDisplayVideoFrameSync then
        m_deckLinkOutput.StartScheduledPlayback(0, m_TimeScale, 1.0)
      else
        m_deckLinkOutput.StopScheduledPlayback(0, StopTime, m_TimeScale);
    end;
    
    procedure TFormMain.CheckBoxExtendedLoggingClick(Sender: TObject);
    begin
      DoExtendedLogging := CheckBoxExtendedLogging.Checked;
    end;
    
    
    procedure TFormMain.CheckBoxRenderFromMemoryClick(Sender: TObject);
    begin
      DoRenderFromMemory := CheckBoxRenderFromMemory.Checked;
    end;
    
    procedure TFormMain.CheckBoxShowPrevClick(Sender: TObject);
    begin
      GLSceneViewer1.Visible := CheckBoxShowPrev.Checked;
    end;
    
    procedure TFormMain.FormCreate(Sender: TObject);
    var
      Res: HRESULT;
      DeckLinkIterator: IDeckLinkIterator;
      DeckLink: IDeckLink;
      deckLinkAPIInformation: IDeckLinkAPIInformation;
      deckLinkVersion: Int64;
        dlVerMajor, dlVerMinor, dlVerPoint: Integer;
      numDevices: Integer;
      deviceNameBSTR: Widestring;
    var
        Spr : TGLSprite;
      I : Integer;
      MediaPath : String;
    begin
      SetGLSceneMediaDir;
        // Load texture for sprite2, this is the hand-coded way using a PersistentImage
        // Sprite1 uses a PicFileImage, and so the image is automagically loaded by
        // GLScene when necessary (no code is required).
        // (Had I used two PicFileImage, I would have avoided this code)
      GLMaterialLibrary1.TexturePaths := GetCurrentDir;
      MediaPath := GLMaterialLibrary1.TexturePaths + '\';
      Sprite1.Material.Texture.Image.LoadFromFile(MediaPath + 'test.png');
      GLMaterialLibrary1.Materials[0].Material.Texture.Image.LoadFromFile('test.png');
        // New sprites are created by duplicating the template "sprite2"
        for i:=1 to 9 do begin
            spr:=TGLSprite(DummyCube1.AddNewChild(TGLSprite));
            spr.Assign(Sprite2);
        end;
      GLCamera1.FocalLength := 1800*50/333;
    
      DoExtendedLogging := CheckBoxExtendedLogging.Checked;
      DoRenderFromMemory := CheckBoxRenderFromMemory.Checked;
    
      // COM-Schnittstelle für Decklink initalisieren:
      Res := CoInitialize(nil);
      if FAILED(Res) then
      begin
        AddToLog(format('Initialization of COM failed - result = %08x.', [Res]));
        Exit;
      end;
    
      // Decklink Iterator:
      Res := CoCreateInstance(CLASS_CDeckLinkIterator, nil, CLSCTX_ALL, IID_IDeckLinkIterator,  DeckLinkIterator);
      if FAILED(Res) then
      begin
        AddToLog(('A DeckLink iterator could not be created.  The DeckLink drivers may not be installed.'));
        if Assigned(DeckLinkIterator) then
          deckLinkIterator := nil;
      end;
    
      // Decklink API-Informationen:
      Res := DeckLinkIterator.QueryInterface(IID_IDeckLinkAPIInformation, DeckLinkAPIInformation);
      if Succeeded(Res) then
      begin
        deckLinkAPIInformation.GetInt(BMDDeckLinkAPIVersion, deckLinkVersion);
        dlVerMajor := (deckLinkVersion and $FF000000) shr 24;
        dlVerMinor := (deckLinkVersion and $00FF0000) shr 16;
        dlVerPoint := (deckLinkVersion and $0000FF00) shr 8;
        AddToLog((Format('DeckLinkAPI version: %d.%d.%d', [dlVerMajor, dlVerMinor, dlVerPoint])));
      end;
    
      // Decklink devices:
      numDevices := 0;
      while (deckLinkIterator.Next(DeckLink) = S_OK)  do
      begin
        // Print the model name of the DeckLink card
        Res := deckLink.GetModelName(deviceNameBSTR);
        if Res = S_OK then
        begin
          Inc(numDevices);
          AddToLog((Format('Found Blackmagic device: %s', [deviceNameBSTR])));
          // Decklink initialisieren:
          SetupDecklink(DeckLink);
        end;
    
        if Assigned(deckLink) then
          DeckLink := nil;
      end;
    
      if Assigned(DeckLinkIterator) then
        DeckLinkIterator := nil;
    
      if (numDevices = 0) then
         AddToLog('No Blackmagic Design devices were found.');
    
     end;
    
    procedure TFormMain.FormDestroy(Sender: TObject);
    begin
      Application.OnIdle := nil;
    
      m_deckLinkOutput.DisableVideoOutput;
      m_DeckLinkKeyer := nil;
      m_deckLinkOutput := nil;
      m_ScheduledCallback := nil;
    
      // Uninitalize COM on this thread
      CoUninitialize;
    end;
    
    procedure TFormMain.GLAsyncTimer1Timer(Sender: TObject);
    begin
      //
    end;
    
    procedure TFormMain.GLCadencer1Progress(Sender: TObject; const deltaTime,
      newTime: Double);
    var
        i : Integer;        a, aBase : Double;
    begin
       // angular reference : 90° per second <=> 4 second per revolution
        aBase:=90*newTime;
       // "pulse" the star
       a:=DegToRad(aBase);
       Sprite1.SetSquareSize(4+0.2*cos(3.5*a));
        // rotate the sprites around the yellow "star"
        for i:=0 to DummyCube1.Count-1 do begin
            a:=DegToRad(aBase+i*8);
            with (DummyCube1.Children[i] as TGLSprite) do begin
             // rotation movement
                Position.X:=4*cos(a);
                Position.Z:=4*sin(a);
             // ondulation
          Position.Y:=2*cos(2.1*a);
                // sprite size change
                SetSquareSize(2+cos(3*a));
            end;
        end;
    end;
    
    procedure TFormMain.GLMemoryViewer1AfterRender(Sender: TObject);
    var
      pFrame: Pointer;
      Res: HRESULT;
    begin
      Exit;
      Caption := Format('%.1f', [GLMemoryViewer1.Buffer.FramesPerSecond]);
    
      m_deckLinkOutput.CreateVideoFrame(m_frameWidth, m_frameHeight, m_frameWidth * 4, bmdFormat8bitBGRA, bmdFrameFlagFlipVertical, m_videoFrameGDI);
    
      m_videoFrameGdi.GetBytes(pFrame);
      GLMemoryViewer1.Buffer.RenderingContext.Activate;
    
    
      try
        //pFrame := AllocMem(m_frameWidth * m_frameHeight * 4);
        GL.ReadPixels(0, 0, m_frameWidth, m_frameHeight, GL_BGRA, GL_UNSIGNED_BYTE , pFrame);
      finally
    
        GLMemoryViewer1.Buffer.RenderingContext.Deactivate;
      end;
    
      with GLMemoryViewer1.Buffer.CreateSnapShotBitmap do
      begin
        SaveToFile('C:\test.bmp');
        Free;
      end;
    
      Res := m_deckLinkOutput.DisplayVideoFrameSync(m_videoFrameGDI);
      if FAILED(RES) then
      begin
        if DoExtendedLogging then
        begin
          case Res of
            E_OUTOFMEMORY: AddToLog('Too many frames are already scheduled');
            E_FAIL: AddToLog('Failure');
            E_ACCESSDENIED: AddToLog('The video output is not enabled.');
            E_INVALIDARG: AddToLog('The frame attributes are invalid.');
          else
            AddToLog('Unknown Failure');
          end;
        end;
      end else
      begin
        if DoExtendedLogging then
          AddToLog('OK');
        //Inc(m_TotalFrames);
      end;
       //Dispose(pFrame);
     //Application.ProcessMessages;
    end;
    
    procedure TFormMain.GLMemoryViewer1PostRender(Sender: TObject);
    var
      pFrame: Pointer;
      Res: HRESULT;
    begin
      m_deckLinkOutput.CreateVideoFrame(m_frameWidth, m_frameHeight, m_frameWidth * 4, bmdFormat8bitBGRA, bmdFrameFlagFlipVertical, m_videoFrameGDI);
    
      m_videoFrameGdi.GetBytes(pFrame);
      //GLMemoryViewer1.Buffer.RenderingContext.Activate;
    
    
      try
        //pFrame := AllocMem(m_frameWidth * m_frameHeight * 4);
        GL.ReadPixels(0, 0, m_frameWidth, m_frameHeight, GL_BGRA, GL_UNSIGNED_BYTE , pFrame);
      finally
    
        //GLMemoryViewer1.Buffer.RenderingContext.Deactivate;
      end;
    
     { with GLMemoryViewer1.Buffer.CreateSnapShotBitmap do
      begin
        SaveToFile('C:\test.bmp');
        Free;
      end;  }
    
      Res := m_deckLinkOutput.DisplayVideoFrameSync(m_videoFrameGDI);
      if FAILED(RES) then
      begin
        if DoExtendedLogging then
        begin
          case Res of
            E_OUTOFMEMORY: AddToLog('Too many frames are already scheduled');
            E_FAIL: AddToLog('Failure');
            E_ACCESSDENIED: AddToLog('The video output is not enabled.');
            E_INVALIDARG: AddToLog('The frame attributes are invalid.');
          else
            AddToLog('Unknown Failure');
          end;
        end;
      end else
      begin
        if DoExtendedLogging then
          AddToLog('OK');
        //Inc(m_TotalFrames);
      end;
       //Dispose(pFrame);
     //Application.ProcessMessages;
    
    end;
    
    procedure TFormMain.GLSceneViewer1AfterRender(Sender: TObject);
    var
      pFrame: Pointer;
      Res: HRESULT;
    begin
      if not DoRenderFromMemory or not GLSceneViewer1.Buffer.RenderingContext.GL.W_ARB_pbuffer then
      begin
        if DoRenderFromMemory and not GLSceneViewer1.Buffer.RenderingContext.GL.W_ARB_pbuffer then
          AddToLog('W_ARB_pbuffer not supported...');
    
        Caption := Format('%.1f', [GLSceneViewer1.Buffer.FramesPerSecond]);
    
        m_deckLinkOutput.CreateVideoFrame(m_frameWidth, m_frameHeight, m_frameWidth * 4, bmdFormat8bitBGRA, bmdFrameFlagFlipVertical, m_videoFrameGDI);
    
        m_videoFrameGdi.GetBytes(pFrame);
        GLSceneViewer1.Buffer.RenderingContext.Activate;
        //GLSceneViewer1.Buffer.RenderingContext.SwapBuffers;
    
        try
          //pFrame := AllocMem(m_frameWidth * m_frameHeight * 4);
          GL.ReadPixels(0, 0, m_frameWidth, m_frameHeight, GL_BGRA, GL_UNSIGNED_BYTE , pFrame);
        finally
    
          GLSceneViewer1.Buffer.RenderingContext.Deactivate;
        end;
    
        Res := m_deckLinkOutput.DisplayVideoFrameSync(m_videoFrameGDI);
        if FAILED(RES) then
        begin
          if DoExtendedLogging then
          begin
            case Res of
              E_OUTOFMEMORY: AddToLog('Too many frames are already scheduled');
              E_FAIL: AddToLog('Failure');
              E_ACCESSDENIED: AddToLog('The video output is not enabled.');
              E_INVALIDARG: AddToLog('The frame attributes are invalid.');
            else
              AddToLog('Unknown Failure');
            end;
          end;
        end else
        begin
          if DoExtendedLogging then
            AddToLog('OK');
          //Inc(m_TotalFrames);
        end;
      end else
      begin
        GLMemoryViewer1.Render;
      end;
    end;
    
    procedure TFormMain.RBKeyingOffClick(Sender: TObject);
    begin
      if (m_DeckLinkKeyer <> nil) then
      begin
        m_DeckLinkKeyer.Disable;
        AddToLog('Keying = off');
      end;
    end;
    
    procedure TFormMain.RBInternalKeyingClick(Sender: TObject);
    begin
     if (m_DeckLinkKeyer <> nil) then
      begin
        m_DeckLinkKeyer.Enable(0);
        m_DeckLinkKeyer.SetLevel(100);
        AddToLog('Keying = internal');
      end;
    end;
    
    procedure TFormMain.RBExternalKeyingClick(Sender: TObject);
    begin
      if (m_DeckLinkKeyer <> nil) then
      begin
        m_DeckLinkKeyer.Enable(1);
        m_DeckLinkKeyer.SetLevel(100);
        AddToLog('Keying = external');
      end;
    end;
    
    procedure TFormMain.SetupDecklink(DeckLink: IDeckLink);
    var
      dm: _BMDDisplayMode;
      sup: _BMDDisplayModeSupport;
      modeNameBSTR: WideString;
      DeckLinkdisplayMode: IDeckLinkdisplayMode;
    begin
      m_deckLinkOutput := nil;
      m_videoFrameGdi := nil;
      deckLinkDisplayMode := nil;
      m_DeckLinkKeyer := nil;
      m_TotalFrames := 0;
      m_frameWidth := 0;
      m_frameHeight := 0;
      m_UseDisplayVideoFrameSync := True;
      m_ScheduledCallback := nil;
    
        // Decklink initalisieren:
      if (DeckLink.QueryInterface(IID_IDeckLinkOutput, m_deckLinkOutput) <> S_OK) then
      begin
        AddToLog('Could not obtain the IDeckLinkOutput interface');
        if Assigned(m_deckLinkOutput) then
          m_deckLinkOutput := nil;
      end else
      begin
        dm := bmdModeHD1080i50;
    
        m_deckLinkOutput.DoesSupportVideoMode(dm, bmdFormat8bitBGRA, bmdVideoOutputFlagDefault, sup, DeckLinkdisplayMode);
        if sup = bmdDisplayModeSupported then
        begin
    
          if (m_deckLinkOutput.EnableVideoOutput(dm, bmdVideoOutputFlagDefault)<>S_OK) then
          begin
            AddToLog('Could not enable Video output');
            if Assigned(m_deckLinkOutput) then
              m_deckLinkOutput := nil;
          end else
          begin
            // Decklink Keyer erzeugen:
            if DeckLink.QueryInterface(IID_IDeckLinkKeyer, m_DeckLinkKeyer) <> s_OK then
            begin
              AddToLog('Keying not available');
              if Assigned(m_DeckLinkKeyer) then
                m_DeckLinkKeyer := nil;
            end;
    
            m_frameWidth := DeckLinkdisplayMode.GetWidth;
            m_frameHeight := DeckLinkdisplayMode.GetHeight;
            DeckLinkdisplayMode.GetFrameRate(m_frameDuration, m_TimeScale);
            DeckLinkdisplayMode.GetName(modeNameBSTR);
            AddToLog(format('Using video mode: %s, width: %d, height: %d, frameDuration: %d, TimeScale: %d', [modeNameBSTR, m_frameWidth, m_frameHeight, m_frameDuration, m_TimeScale]));
    
            // Get a BGRA frame
            if (m_deckLinkOutput.CreateVideoFrame(m_frameWidth, m_frameHeight, m_frameWidth * 4, bmdFormat8bitBGRA, bmdFrameFlagFlipVertical, m_videoFrameGDI) <> S_OK) then
            begin
              AddToLog('Could not obtain the IDeckLinkOutput CreateVideoFrame interface');
              if Assigned(m_videoFrameGDI) then
                m_videoFrameGDI := nil;
            end else
            begin
              //
            end;
    
            m_ScheduledCallback := TCallBackProc.Create;
            m_deckLinkOutput.SetScheduledFrameCompletionCallback(m_ScheduledCallback);
    
          end;
        end;
      end;
    end;
    
    procedure TFormMain.Timer1Timer(Sender: TObject);
    begin
      Caption := Format('%.1f', [GLMemoryViewer1.Buffer.FramesPerSecond]);
      GLMemoryViewer1.Buffer.ResetPerformanceMonitor;
    end;
    
    { TCallBackProc }
    function TCallBackProc.QueryInterface(const IID: TGUID; out Obj): HResult;
    const
      IID_IUnknown : TGUID = '{00000000-0000-0000-C000-000000000046}';
    begin
      Result := E_NOINTERFACE;
      Pointer(Obj):=nil;
      if IsEqualGUID(IID, IID_IUnknown)then
      begin
        Pointer(Obj) := Self;
        _addRef;
        Result := S_OK;
      end else
      if IsEqualGUID(IID, IDeckLinkInputCallback)then
      begin
        //GetInterface(IDeckLinkInputCallback, obj);
        Pointer(Obj) := Pointer(IDeckLinkVideoOutputCallback(self));
        _addRef;
        Result := S_OK;
      end;
    end;
    
    function TCallBackProc.ScheduledFrameCompleted(
      const completedFrame: IDeckLinkVideoFrame;
      res: _BMDOutputFrameCompletionResult): HResult;
    begin
      //completedFrame._Release;
      FormMain.AddToLog('');
    end;
    
    function TCallBackProc.ScheduledPlaybackHasStopped: HResult;
    begin
    
    end;
    
    function TCallBackProc._AddRef: Integer;
    begin
      Result := InterlockedIncrement(m_RefCount);
    end;
    
    function TCallBackProc._Release: Integer;
    var
      newRefValue : integer;
    begin
      newRefValue:=InterlockedDecrement(m_RefCount);
      if newRefValue=0 then
      begin
        //Destroy;
        free;
        Result := 0;
      end;
      result:=newRefValue;
    
    end;
    
    end.
    
     

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.