Jump to content

Renate Schaaf

Members
  • Content Count

    161
  • Joined

  • Last visited

  • Days Won

    7

Posts posted by Renate Schaaf


  1. As far as I know TWicImage always uses the alpha-channel if it's present. I tried to reproduce your issue and failed. All my .png with alpha-channel display just fine, and the interpolationmode specified is used.

     

    This is how I tested:

     

    procedure TForm1.Button1Click(Sender: TObject);
    begin
      if not FOD.Execute then
      exit;
      Image1.Picture:=nil;
      Image1.Picture.WicImage.LoadFromFile(FOD.FileName);
      Image1.Picture.WICImage.InterpolationMode:=TWicImageInterpolationMode.wipmLinear;
    end;

    Image1.stretch and Image1.proportional are true, the image has all anchors set so I can test scaling. Maybe I don't understand your problem.


  2. 1 hour ago, Uwe Raabe said:

    Is the code you are working on part of the dpr or inside a unit? If the former, try to move it to a unit and see if that solves the problem.

    I now only kept the minimum in the .dpr code and moved the rest to a procedure in another unit. The crash hasn't happened ever since. Thanks! Now I remember that previous Delphi-versions also had some problems with too much code in the dpr-source ...

     

    1 hour ago, Anders Melander said:

    Sure, but hard to say without seeing your code.

    I'll post my code or try the other suggestion if the error comes back. For now it seems to behave decently. Thanks!


  3. Thanks, Anders. I tried it, and this is what I get as a call stack:

     

    :76cf4044 KERNELBASE.RaiseException + 0x64
    :69e32cae ; c:\program files (x86)\embarcadero\studio\23.0\bin\exceptiondiag290.bpl
    :6d93e468 ; C:\Program Files (x86)\Embarcadero\Studio\23.0\bin\delphicoreide290.bpl
    :6d93e498 ; C:\Program Files (x86)\Embarcadero\Studio\23.0\bin\delphicoreide290.bpl
    :6d93ea68 ; C:\Program Files (x86)\Embarcadero\Studio\23.0\bin\delphicoreide290.bpl
    :6d93d990 ; C:\Program Files (x86)\Embarcadero\Studio\23.0\bin\delphicoreide290.bpl
    :6d93d498 ; C:\Program Files (x86)\Embarcadero\Studio\23.0\bin\delphicoreide290.bpl
    :6ca67850 ; C:\Program Files (x86)\Embarcadero\Studio\23.0\bin\coreide290.bpl
    :6ca67ac1 coreide290.@Codemgr@TCodeManager@CheckParsed$qqrv + 0x4d
    :6ca6795c coreide290.@Codemgr@TCodeManager@SourceIsValid$qqrv + 0x20
    :6d93f58c delphicoreide290.@Pasmgr@TUnitManager@GetRegions$qqrv + 0x28
    :6d8cbe71 delphicoreide290.@Basedelphiproject@TDelphiProjectModuleHandler@GetRegions$qqrx20System@UnicodeString + 0x6d
    :6ca8e536 ; C:\Program Files (x86)\Embarcadero\Studio\23.0\bin\coreide290.bpl
    :6ca8df53 ; C:\Program Files (x86)\Embarcadero\Studio\23.0\bin\coreide290.bpl
    :6c6d0b7f ; C:\Program Files (x86)\Embarcadero\Studio\23.0\bin\coreide290.bpl
    :6e700208 ; C:\Program Files (x86)\Embarcadero\Studio\23.0\bin\rtl290.bpl
    :6e5e21ce rtl290.@System@@Assert$qqrx20System@UnicodeStringt1i + 0x66
    :75655d49 KERNEL32.BaseThreadInitThunk + 0x19
    :7792d6db ntdll.RtlInitializeExceptionChain + 0x6b
    :7792d661 ;

     

    And this is the error reported (rough English translation included):

     

    Erste Gelegenheit für Exception bei $76CF4044. Exception-Klasse EParseError mit Meldung 'Ungültiger Prozedur-Header'. Prozess bds.exe (13328)

    First opportunity for exception at $76CF4044. Exception-class EParseError with description 'Invalid procedure header'.

     

    Could that be something in my code causing this? 


  4. I'm trying to make a console application, but the IDE crashes frequently to desktop without showing any error code. This never happens in any VCL- or FMX- project. Once I manage to get the project into a compilable state it compiles and runs just fine, no hints, no warnings, no exceptions. The crash could happen when I just move the mouse over the code or hit enter to start a new line. What can I do to prevent this? I've tried to disable error-insight without effect.

     

    I'm on the 12.1 CE running under Windows 11.

     

    I forgot to say that I already disabled refactoring, since that caused a stackoverflow in the IDE.


  5. On 9/25/2025 at 5:51 PM, Kas Ob. said:

    but to fix it you need to pinpoint the cause.

    I think I found the setting with the potential for errors and hangs: It's the source-reader attribute

     

    MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING.

     

    It allows the source-reader to decode to a new size, change pixel-aspect and framerate. If I change it to

     

    MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING,

     

    all the videos that worked under Win10 can be processed under Win11 too. And the encoder does framerate-conversion automagically! Problem is, now I have to resize the output myself, which is causing a slowdown. I solved it for the time being by having a slow but safe procedure AddVideo and one which is fast but might fail. I'll also implement the test based on Remy's idea. And then I hope that the next Win-Update will fix the problems.

    • Like 1

  6. 16 hours ago, Remy Lebeau said:

    Have you considered moving your audio processing into a separate process of its own that your main program can communicate with. If the processing times out then your main program can kill and restart that separate process.  That way, you don't have to kill your main program.

    That's a good idea. But my project is a utility class which combines pictures and video-clips into a video, so there's no application per se. I could write a helper-procedure for testing an input-file in a separate process, and if that doesn't return, give the user an option to blacklist that file. (Once I figure out how to do that.) Otherwise the logic of what I have to run in a separate process is a bit beyond me 🙂

     

    Thanx!


  7. 16 hours ago, Kas Ob. said:

    see what codec are installed on 32bit and 64bit, if you doubt then delete them !

    I had k-lite installed, but seeing it didn't contain anything for mf, I deinstalled it right away. The autorun-tool doesn't show anything in codecs, if I filter out microsoft. The ac-4 decoder is available in an appxbundle on microsoft store. For the rest of your comments, I'll have to do some more research.

     

    Thanx!


  8. 32 minutes ago, Kas Ob. said:

    I don't think it is possible to recover from this

    It might get fixed, because a file for which ReadSample hangs, sometimes returns an error code on ReadSample, which is

    MF_E_NOT_AVAILABLE                    = _HRESULT_TYPEDEF_($C00D36D6)

    This seems to happen, if mf thinks that the input is protected, when actually it isn't. And that issue has been reported. 

    Also, since you mention hardware acceleration, software encoding now never works. It returns with a sharing violation for a device. No idea what that could be. 

    Being at the mercy of a new operating system almost makes me want to throw away the whole project. 

    There are decoders which are no longer implemented by default (dolby ac-x), they can be downloaded but are very hard to find...


  9. 22 minutes ago, Lars Fosdal said:

    The price you are willing to pay for your primary hobby, with the features you expect to be available for you (I am still baffled why Linux is not available in the lower tiers - it just doesn't make sense).  Exactly where that pricepoint lies - is hard to say.

    I'd be ready to pay around 800-1000€ for a perpetual pro-license for my hobby, just to make sure I can still use Delphi when they decide to ditch the CE. But not double of that.

    • Like 1

  10. Ever since I work on my new Win 11 system, mediafoundation is acting up. (I use mfpack). Apparently microsoft is aware of some issues and has promised to fix them.

     

    But right now the worst thing is that sometimes a call (IMFSourceReader.ReadSample) just doesn't return. The setup of the reader generates no errors, and it's not possible to see what is causing the hang in the dll (mfReadWrite.dll).

     

    I've tried to intercept this by moving the call into a task and generating an exception on timeout. This is of course terrible for performance in a call which is used very often, but now at least I get an error message when it occurs. Still, the application hangs and the only way to solve this is to kill the application in taskmanager. 

     

    Is there another way to recover from this, or do I just have to tell users "Sorry this file is not supported, go to taskmanager and terminate the application and never use this file again."?


  11. This way it works. Don't set the parent, set DimmedControl to self. The sizes and the anchors are set automatically when active is set to true.

    procedure TForm1.Button1Click(Sender: TObject);
    begin
     TransparentPanel := TDimPanel.Create(Self);
     TransparentPanel.DimmedControl:=self;
     TransparentPanel.Alpha := 140;
     TransparentPanel.DimColor := clBlack;
     TransparentPanel.Active := True;
    
    end;

    Note that once activated, the panel is on top of its dimmedControl, so you would not be able to click on anything on the form. The use of the panel is to temporarily disable input to a part of the application and to give the user visible feedback about it. It acts like a semitransparent glass layer on top of the dimmed window. Looks like you don't want to use it that way. 

    • Thanks 1

  12. Yes, the update on resize should work automatically.  For completeness' sake I'm posting the code of the Dimpanel-version which I presently use. I introduced a property Active, which is decoupled from the Visible-property and a propert DimmedControl which is decoupled from the Parent-property. Also, this version should work on Delphi 2006 and up. To dim the whole form this code works:

     

    procedure TForm1.Button4Click(Sender: TObject);
    begin
      //Dimmer is a TDimpanel created in OnCreate
      Dimmer.DimColor := clNavy;
      Dimmer.DimmedControl := self;
      Dimmer.Alpha := 150;
      Dimmer.DisableDimmedControlOnActive := true;
      Dimmer.active := true;
    end;

    And here is the code for TDimPanel:

    Unit uDimPanel;
    // by aehimself on https://en.delphipraxis.net/topic/4826-how-to-dim-a-tabsheet/
    
    Interface
    
    Uses Classes,
      Windows,
      ExtCtrls,
      Graphics,
      Controls,
      Messages;
    
    Type
      TDimPanel = Class(TCustomPanel)
      private
        _bitmap, _scr: TBitMap;
        _enabledcontrols: TList;
        fActive: boolean;
        fDimmedControl: TWinControl;
        fDimColor: TColor;
        fDoDisable: boolean;
        fAlpha: Byte;
        Procedure DisableParentControls;
        Procedure EnableParentControls;
        Procedure UpdateBitmap(DoRepaint: boolean);
        procedure SetActive(const Value: boolean);
        procedure SetDimmedControl(const Value: TWinControl);
        procedure WMEraseBkgnd(var Msg: TMessage); message WM_EraseBkgnd;
      protected
        Procedure Paint; Override;
        Procedure Resize; Override;
        Procedure Notification(
          AComponent: TComponent;
          Operation:  TOperation); override;
        Procedure Loaded; Override;
      public
        Constructor Create(inOwner: TComponent); override;
        Destructor Destroy; Override;
    
        // Set Active = true at runtime to dim the DimmedControl
        // Set Active = false to re-enable DimmedControl
        Property Active: boolean read fActive write SetActive;
    
        Property Bitmap: TBitMap read _bitmap; // for debug reasons
        Property Screen: TBitMap read _scr;
      published
        Property DimmedControl: TWinControl read fDimmedControl
          write SetDimmedControl;
        property DimColor: TColor read fDimColor write fDimColor;
        property DisableDimmedControlOnActive: boolean read fDoDisable
          write fDoDisable;
        property Alpha: Byte read fAlpha write fAlpha;
        property Align;
        property OnResize;
        property StyleElements; //comment for unsuitable Delphi-versions
      End;
    
    procedure Register;
    
    Implementation
    
    uses SysUtils;
    
    Procedure Register;
    Begin
      RegisterComponents(
        'Custom',
        [TDimPanel]);
    End;
    
    Constructor TDimPanel.Create(inOwner: TComponent);
    Begin
      inherited Create(inOwner);
      Self.Visible := False;
      // Self.DoubleBuffered := True;
      // Might cause flicker if true, plus we are custom drawing
      Self.ParentBackground := False;
      Self.BevelOuter := bvNone;
      Self.Caption := '';
      fDimColor := clBlack;
      fDoDisable := true;
      fAlpha := 140;
      if (csDesigning in ComponentState) then
        exit;
    
      _bitmap := TBitMap.Create;
      _scr := TBitMap.Create;
    
      _bitmap.PixelFormat := pf24bit;
      _bitmap.Transparent := False;
    
      _scr.PixelFormat := pf24bit;
      _scr.Transparent := False;
    
      _enabledcontrols := TList.Create;
      ControlStyle := ControlStyle + [csOpaque];
    End;
    
    Destructor TDimPanel.Destroy;
    Begin
      if fActive and fDoDisable then
        EnableParentControls;
      fDimmedControl := nil;
      _scr.Free;
      FreeAndNil(_bitmap);
      FreeAndNil(_enabledcontrols);
    
      inherited;
    End;
    
    Procedure TDimPanel.DisableParentControls;
    Var
      a: Integer;
    Begin
      // Should be empty every time, but to be sure...
      _enabledcontrols.Clear;
    
      For a := 0 To Self.Parent.ControlCount - 1 Do
        If (Self.Parent.Controls[a] <> Self) And Self.Parent.Controls[a]
          .Enabled Then
        Begin
          _enabledcontrols.Add(Self.Parent.Controls[a]);
          Self.Parent.Controls[a].Enabled := False;
        End;
    End;
    
    Procedure TDimPanel.EnableParentControls;
    Var
      control: TControl;
      i: Integer;
    Begin
      Try
        For i := 0 to _enabledcontrols.Count - 1 do
        begin
          control := TControl(_enabledcontrols[i]);
          control.Enabled := true;
        end;
      Finally
        _enabledcontrols.Clear;
      End;
    End;
    
    // Loaded is called, when all properties of all components of the owner
    // have been read from the .dfm and have called their setters.
    // Now we can be sure that fDimmedControl has the correct dimensions,
    // and we just call its setter again.
    procedure TDimPanel.Loaded;
    begin
      inherited;
      DimmedControl := fDimmedControl;
    end;
    
    procedure TDimPanel.Notification(
      AComponent: TComponent;
      Operation:  TOperation);
    begin
      inherited;
      if AComponent = fDimmedControl then
        if Operation = opRemove then
          fDimmedControl := nil;
    end;
    
    Procedure TDimPanel.Paint;
    
    Begin
      // Omit the call to inherited in general. We only need a black background
      // and the opaque bitmap we captured earlier.
      if (csDesigning in ComponentState) then
      begin
        inherited;
        exit;
      end;
      if assigned(_bitmap) then
        BitBlt(
          Canvas.Handle,
          0,
          0,
          Width,
          Height,
          _bitmap.Canvas.Handle,
          0,
          0,
          SRCCopy);
    End;
    
    Procedure TDimPanel.Resize;
    Begin
      inherited;
      If Self.Active Then
        Self.UpdateBitmap(true);
    End;
    
    procedure TDimPanel.SetActive(const Value: boolean);
    begin
      // if the parent is not the same as fDimmedControl it doesn't make any sense
      // for example if fDimmedControl=nil ...
      if Self.Parent <> fDimmedControl then
      begin
        fActive := False;
        exit;
      end;
      fActive := Value;
      If Self.fActive Then
      Begin
        // Make sure nothing can be interacted with while parent is dimmed
        if fDoDisable then
        begin
          Self.DisableParentControls;
          // Repaint the parent to reflect disabled state of controls
          Self.Parent.Repaint;
        end;
        Self.UpdateBitmap(False); // no need to repaint the parent at this time
        Self.BringToFront;
        Self.Visible := true;
      End
      Else
      Begin
        // Clear bitmaps to free up memory
        Self.Visible := False;
        _bitmap.SetSize(
          0,
          0);
        _scr.SetSize(
          0,
          0);
        if fDoDisable then
          // Re-enable all controls we disabled earlier
          Self.EnableParentControls;
      end;
    end;
    
    procedure TDimPanel.SetDimmedControl(const Value: TWinControl);
    var
      save: boolean;
    begin
      // Don't check <>, otherwise Loaded won't work!
      // if fDimmedControl <> Value then
      // begin
      fDimmedControl := Value;
      if (csDesigning in Self.ComponentState) then
        exit;
      if assigned(fDimmedControl) then
      begin
        save := Self.Active;
        if fDoDisable then
          // Re-enable disabled controls from previous parent
          // and clear DisabledList
          Self.EnableParentControls;
    
        Self.Active := False;
    
        Self.Parent := fDimmedControl;
        Self.Align := alNone; // clear any align set at design time
        Self.SetBounds(
          0,
          0,
          Parent.ClientWidth,
          Parent.ClientHeight);
        Self.Anchors := [akLeft, akTop, akRight, akBottom];
    
        // Re-activate if necessary
        Self.Active := save;
      end
      else
      begin
        Active := False;
        Parent := nil;
      end;
    end;
    
    // Replace shr 8 by div 256, so we don't get a range check error.
    // Turn optimization on, so div 256 runs as fast as shr 8
    // The optimizer sees that 256 is a power of 2.
    {$IFOPT O- }
    {$DEFINE O_MINUS }
    {$O+ }
    {$ENDIF }
    {$IFOPT Q+}
    {$DEFINE Q_PLUS}
    {$Q-}
    {$ENDIF}
    
    
    // AlphaBlend Source and Target using alpha/255 on Target, 1-alpha/255 on Source
    // and store result in target.
    procedure Alphablend(
      Source, Target: TBitMap;
      Alpha:          Byte);
    var stride: Integer;
      ps, pt: PByte;
      i: Integer;
    begin
      Assert(Source.PixelFormat = pf24bit);
      Assert(Target.PixelFormat = pf24bit);
      Assert(Source.Width = Target.Width);
      Assert(Source.Height = Target.Height);
      stride := ((Source.Width * 24 + 31) and not 31) div 8;
      ps := Source.ScanLine[Source.Height - 1];
      pt := Target.ScanLine[Target.Height - 1];
      for i := 1 to Source.Height * stride do
      begin
        pt^ := ps^ + (Alpha * (pt^ - ps^)) div 256;
        inc(pt);
        inc(ps);
      end;
    end;
    
    // Restore optimization to original
    {$IFDEF O_MINUS}
    {$O-}
    {$UNDEF O_MINUS}
    {$ENDIF}
    {$IFDEF Q_PLUS}
    {$Q+}
    {$UNDEF Q_PLUS}
    {$ENDIF}
    
    
    Procedure TDimPanel.UpdateBitmap(DoRepaint: boolean);
    Var
      dc: HWND;
    Begin
      if (csDesigning in ComponentState) then
        exit;
      If Self.Active Then
      Begin
        if DoRepaint then
        begin
          // If the dimpanel is visible, it will be included in the screenshot. So
          // let's "hide" it...
          Self.Visible := False;
          // ...and kindly ask the parent to repaint so new dimensions can be
          // captured correctly!
    
          Self.Parent.Repaint;
        end;
      End;
      Try
        _bitmap.SetSize(
          Self.Parent.ClientWidth,
          Self.Parent.ClientHeight);
        _scr.SetSize(
          _bitmap.Width,
          _bitmap.Height);
        dc := GetDC(Self.Parent.Handle);
        Try
          BitBlt(
            _scr.Canvas.Handle,
            0,
            0,
            _bitmap.Width,
            _bitmap.Height,
            dc,
            0,
            0,
            SRCCopy);
        Finally
          ReleaseDC(
            Self.Parent.Handle,
            dc);
        End;
        _bitmap.Canvas.Brush.Color := fDimColor;
        _bitmap.Canvas.FillRect(_bitmap.Canvas.ClipRect);
        Alphablend(
          _scr,
          _bitmap,
          Alpha);
      Finally
        If Self.Active Then
          if DoRepaint then
            Self.Visible := true;
      End;
    End;
    
    procedure TDimPanel.WMEraseBkgnd(var Msg: TMessage);
    begin
      Msg.Result := 1;
    end;
    
    End.

     

    • Thanks 1

  13. 1 hour ago, Freeeee said:

    putting variables in the PFR file  didn't occur to me.

    What do you mean by that? Do you mean the .dfm-file? I certainly did not suggest any such thing! You leave that alone (until you know what you're doing:), or there's hell to pay! The variables I put into the private part of the form-class in the form-unit. Please read my example. The .dfm-file is where the designer stores all the properties of the form and the controls and components it uses. If you change anything in that the form may become unusable.

     

    1 hour ago, Freeeee said:

    if any one has written a  Delphi Cook Book with all of the 

    reserved words explained and used in a non-trivial way.     If you know of one, please let me know.

    My favorite is "Delphi in a nutshell" by Ray Lischner, a bit aged now, of course, but great as a reference.

     

     


  14. 10 hours ago, Freeeee said:

    I just ran into a situation where I was doing just that with indices into an array.  The compiler tells me that they can NOT

    be global

    The compiler was probably not complaining for the reason you thought it did. Without code impossible to tell.

    10 hours ago, Freeeee said:

    How would you fill an array with  user input data.?  Other than ALL at Once.

    I don't like lengthy explanations, so here is an example to get you started:

    program FillTestArray;
    
    {$APPTYPE CONSOLE}
    
    {$R *.res}
    
    uses
      System.SysUtils;
    
    var TestArray: array[0..3] of string;
        i: integer;
    
    begin
      try
       for i := 0 to 3 do
         begin
           write('Type input for ' + i.ToString +': ');
           readln(TestArray[i]);
         end;
       for i := 0 to 3 do
       begin
         write('TestArray['+ i.ToString +']: '+TestArray[i]);
         readln;
       end;
      except
        on E: Exception do
          Writeln(E.ClassName, ': ', E.Message);
      end;
    end.

     

     

    10 hours ago, Freeeee said:

    First Question:       Do I have to make S a global 'variable' to use it the way I'm intending.

    Make them fields of the form, that's the logical thing, since they are only used in that context. I've changed your unit, read my comments.

    Also, if you attach a form-unit, include the .dfm and zip it up. Makes it ever so much easier to give you an answer.

     

    unit Unit2;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.StdCtrls;
    
    type
      TTest = class(TForm)
        MySG: TStringGrid;
        CBCycle: TButton;
        procedure FormCreate(Sender: TObject);
        procedure CBCycleClick(Sender: TObject);
        procedure MySGClick(Sender: TObject);
      private
        switch, A, B: integer; //make these variables of the form
        S: string;
        procedure AdjustMySg;  //make this a method of the form
        { Private-Deklarationen }
      public
        { Public-Deklarationen }
      end;
    
      //This variable should only be used for auto-creation of the form.
      //Never use it in your code.
      //If you can comment these lines, and your form-code is still error-free
      //you've done it right.
    var
      Test: TTest;
    
    implementation
    
    {$R *.dfm}
    
    Procedure TTest.AdjustMySg;
    var I : integer;
    begin
      MySg.ColCount := 6;
      MySg.RowCount := 21;
      MySg.DefaultColWidth := 100;
      MySg.Cells[0,0] := 'Test';
      MySg.Cells[1,0] := 'TESTA';
      MySg.Cells[2,0] := 'TESTB';
      MySg.Cells[3,0] := 'TESTC';
      MySg.Cells[4,0] := 'TESTD';
      MySg.Cells[5,0] := 'TESTE';
      for i := 1 to 20 do
        Mysg.Cells[0,i] := inttoStr(i);
    end;
    
    procedure TTest.CBCycleClick(Sender: TObject);
    begin
       switch := switch + A;  // switch starts at zero
    
       If Switch = 1 then     // switch point to last button push
          begin               // or 'the Selected' row
            if B<>0 then      //not first time
              MySg.Cells[1, switch + B]:=S;
            A := 1; B := -1;
            S := MySg.Cells[1, switch];          // S as it 'is'
            MySg.Cells [1, switch] := '***' + S;
          end;
       if (Switch > 1) and (switch <= 20) then  // include last row
          begin
            MySg.Cells [1, switch + B] := S;   //replase '***' + S w just S
            S := MySg.Cells [1, Switch];        // new S
            MySg.Cells [1, switch] := '***' + S;  // show as 'selected'.
          end;
       If (Switch = 20) then
          begin
             A := -1 ; B := 1;
          end;
    end;
    
    procedure TTest.FormCreate(Sender: TObject);
    begin
      AdjustMySg;    //Do any initialization here
      switch := 0;
      A := 1;
      B := 0;
    end;
    
    procedure Ttest.MySGClick(Sender: TObject);
    Var  I, J, K : Integer;
    begin
      AdjustMySg;
      K := 23;
      with MySG do
        for I := 1 to ColCount - 1 do
          for J:= 1 to RowCount - 1 do
            begin
              K := K + 1;
              Cells[i, j] := IntToStr(k);
            end;
     MySG.Cells[1,1] := 'longtest';
     MySg.Cells[2,1] := 'Blongtest';
     MySg.Cells[3,1] := 'Clongtest';
     MySg.Cells[4,1] := ' short';
     MySg.Cells[5,1] := IntToStr(k);
    end;

    You could replace your unit2 by this one. In the designer you then need to attach TTest.FormCreate to the OnCreate event of the form.

     

    To the rest of your questions: No, and there are better ways to achieve what you want.

    When I changed from Borland-Pascal to Delphi (30 years ago) I read the manuals front to back, and then I thought I knew what I was doing (wrong). Don't get discouraged, read up on the stuff, download some simple sample projects and understand what they are doing!

     

    Renate

     


  15. if (Switch > 1) and (switch < 20) then
          begin
            MySg.Cells [1, switch + B] := S;   //replase '***' + S w just S

     

    S is a local variable. What it contains is undefined at the beginning of the procedure and independent of what you assigned to it in the previous click. Use the debugger, if you have a problem like you describe. Step through your procedure, and examine the variables. You will see why your code is not doing what you expect. 

    The variable B has the same problem.


  16. I managed to fix the root of all timing- and video-stuttering problems. It was me using a global variable for the media-buffer passed to the video-samples. Apparently this is not very threadsafe (who would have guessed :classic_huh:). Now there is no more need to put sleeps all over the code or to change the timer-resolution. As a side-effect, hardware-encoding got quite a bit faster.

     

    I also added support for encoding audio to (lossless) FLAC. Check it out on https://github.com/rmesch/Bitmaps2Video-for-Media-Foundation if you're interested.

     

    Have a nice weekend

     

    Renate

     

    I forgot to say that this version requires the newest version of Mfpack https://github.com/FactoryXCode/MfPack

    • Like 1

  17. 52 minutes ago, Kas Ob. said:

    yet the result video frames are grouped, so it might be something has to do with WMF and its codec or missing some some settings somewhere

    I've been wondering about the same, maybe the setup isn't right. But I find it so hard to even figure out what settings you can specify. 

     

    56 minutes ago, Kas Ob. said:

    2) the duration at 1000 and i am not talking about the timestamp but the relevancy of nominator and video frames is 1000, i tried to tweak things and it didn't change, even used the recommended 10m instead of 1m you are using, still didn't change, so this also might be like above a setting or a constrained bit/frame/packet limitation specific to this very codec, one test video is 60gps with 200 duration, the output is 1000 at 30fps, while it should be 400.

    You lost me here. What 10m, and what's gps?

     

    58 minutes ago, Kas Ob. said:

    Anyway, here a nice answer on SO leading to very beautiful SDK, you might find it very useful

    Indeed, that looks interesting. Thanks, your links have already helped me a lot to understand better.

     

    Renate


  18. I think I solved the audio-syncing  ... kind of.

     

    First observation: Audio and video are perfectly synced if the audio comes from a .wav-file. You can check this using the optimal frame-rates 46.875 or 31.25. So for optimal synching, compressed audio should be converted to .wav first. I have added a routine in uTransformer.pas which does this. In the demo there are some checkboxes to try this out.

     

    Second observation: For compressed input the phase-shift in audio happens exactly at the boundaries of the IMFSamples read in. So this is what I think happens: The encoder doesn't like the buffer-size of these samples and throws away some bytes at the end.

    This causes a gap in the audio-stream and a phase-shift in the timing. I have a notorious video where you can actually hear these gaps after re-encoding. If I transform the audio to .wav first, the gaps are gone. One could try to safekeep the thrown-away bytes and pad them to the beginning of the next sample, fixing up the time-stamps... Is that what you were suggesting,  @Kas Ob.? Well, I don't think i could do it anyway :).

     

    So right now, first transforming audio-input to .wav is the best I can come up with. For what I use this for it's fine, because I mix all the audio into one big .wav before encoding. 

     

    Renate

    • Like 1

  19. There is a new version at https://github.com/rmesch/Bitmaps2Video-for-Media-Foundation.

     

    New stuff: Some rewrite of audio, making sure that gaps at the beginning of a stream are filled with silence. 2 optimized frame-rates for audio-synching, see below. Most importantly:

    One can now run @Kas Ob.'s frame analysis from within the demo, if one enables the hidden tab "Analysis". I just made the lines a bit shorter, as the rest was just repeating the same values for all I tested, as far as I could see. The file ffprobe.exe needs to be in the same directory as DemoWMF.exe. ffprobe is part of ffmpeg-git-essentials.7z on https://www.gyan.dev/ffmpeg/builds/.

     

    I spent a good amount of time trying to figure out what I can and what I cannot control about audio-synching, tracing into the relevant code and running the analysis. Results of audio-rethynching follow (beware, it's long):


    The math is for audio-sample-rate of 48000 and the time units are all s.

    Audio-blockalign is always 4 Bytes for what I do. 

     

    There are at least 2 different meanings of "sample":

    PCMSample: as in samples per second. ByteSize: Channels*BitsPerSample/8 = 2*16/8 = 4 Bytes. Time: 1/48000 s

     

    IMFSample: Chunk of audio returned by IMFSourceReader.ReadSample. It contains a buffer holding a certain amount of uncompressed PCMsamples, and info like timestamp, duration, flags ...

    The size of these samples varies a lot with the type of input. Some observed values:

     

    .mp3-file 1: 
    Buffersize = 96 768 Bytes    Duration = 0.504  (96768 bytes = 96768/4 PCMSamples = 96768/4/48000 s OK)
    .mp3-file 2:
    Buffersize = 35 108 Bytes    Duration = 0.1828532 (35108/4/48000 = 0.182854166.. not OK) 
    .wmv-file:
    Buffersize = 17 832 Bytes    Duration = 0.092875  (17832/4/48000 = 0.092875 OK)

     

    Except for the first sample read, the values don't differ from sample to sample. Those are the samples I can write to the sinkwriter for encoding. Breaking them up seems like a bad idea. I have to trust MF to handle the writing correctly. The buffers seem to always be block-aligned. I've added some redundant variables in TBitmapEncoderWMF.WriteAudio so these values can be examined in the debugger.

     

    A related quantity are audio-frames. Similarly to the video-stream the audio-stream of a compressed video consists of audio-frames. 1 audio-frame contains the compressed equivalent of 1024 PCMSamples. So:

    AudioFrameDuration = 1024/48000   AudioFrameRate = 48000/1024

     

    I can only control the writing of the video by feeding the IMFSamples of video and audio to the sinkwriter in good order. The samples I write to the sinkwriter are collected in a "Leaky-Bucket"-buffer. The encoder pulls out what it needs to write the next chunk of video. It hopefully waits until there are enough samples to write something meaningful. Problems arise if the bucket overflows. There need to be enough video- and audio-samples to correctly write both streams.

     

    So here is the workflow, roughly (can be checked by stepping into TBitmapEncoderWMF.WriteOneFrame):

      Check if the audio-time written so far is less than the timestamp of the next video-frame.
         Yes: Pull audio-samples out of the sourcereader and write them to the sinkwriter until audio-time >= video-timestamp.
              Looking at the durations above, one sample might already achieve this.
         Write the next video-frame
    Repeat

    In the case of mp3-file 1 the reading and writing of 1 audio-sample would be followed by the writing of several video-samples.

     

    The encoder now breaks the bucket-buffer up into frames, compresses them and writes them to file. It does that following its own rules, which I have no control over. Frame-analysis can show the result: 

    A group of video-frames is followed by a group of audio-frames, which should cover the same time-interval as the video-frames. In the output I have seen so far, the audio-frame-period is always 15 audio-frames. For video-framerate 30, the video-frame-period is 9 or 10 frames. Why doesn't it make the audio- and video-periods smaller? No idea. Guess that's the amount of info the players can handle nowadays, and these periods are a compromise between optimal phase-locking of audio- video- periods and the buffer-size the player can handle. Theoretically, at framerate 30, 16 video-frames should phase-lock with 25 audio-frames.

     

    Here is one of those video-audio-groups. Video-framerate is 30. 

    video stream_index=0 key_frame=0 pts=39000 pts_time=1.300000  duration_time=0.033333
    video stream_index=0 key_frame=0 pts=40000 pts_time=1.333333  duration_time=0.033333
    video stream_index=0 key_frame=0 pts=41000 pts_time=1.366667  duration_time=0.033333
    video stream_index=0 key_frame=0 pts=42000 pts_time=1.400000  duration_time=0.033333
    video stream_index=0 key_frame=0 pts=43000 pts_time=1.433333  duration_time=0.033333
    video stream_index=0 key_frame=0 pts=44000 pts_time=1.466667  duration_time=0.033333
    video stream_index=0 key_frame=0 pts=45000 pts_time=1.500000  duration_time=0.033333
    video stream_index=0 key_frame=0 pts=46000 pts_time=1.533333  duration_time=0.033333
    video stream_index=0 key_frame=0 pts=47000 pts_time=1.566667  duration_time=0.033333
    video stream_index=0 key_frame=0 pts=48000 pts_time=1.600000  duration_time=0.033333

    audio stream_index=1 key_frame=1 pts=62992 pts_time=1.312333  duration_time=0.021333
    audio stream_index=1 key_frame=1 pts=64016 pts_time=1.333667  duration_time=0.021333
    audio stream_index=1 key_frame=1 pts=65040 pts_time=1.355000  duration_time=0.021333
    audio stream_index=1 key_frame=1 pts=66064 pts_time=1.376333  duration_time=0.021333
    audio stream_index=1 key_frame=1 pts=67088 pts_time=1.397667  duration_time=0.021333
    audio stream_index=1 key_frame=1 pts=68112 pts_time=1.419000  duration_time=0.021333
    audio stream_index=1 key_frame=1 pts=69136 pts_time=1.440333  duration_time=0.021333
    audio stream_index=1 key_frame=1 pts=70160 pts_time=1.461667  duration_time=0.021333
    audio stream_index=1 key_frame=1 pts=71184 pts_time=1.483000  duration_time=0.021333
    audio stream_index=1 key_frame=1 pts=72208 pts_time=1.504333  duration_time=0.021333
    audio stream_index=1 key_frame=1 pts=73232 pts_time=1.525667  duration_time=0.021333
    audio stream_index=1 key_frame=1 pts=74256 pts_time=1.547000  duration_time=0.021333
    audio stream_index=1 key_frame=1 pts=75280 pts_time=1.568333  duration_time=0.021333
    audio stream_index=1 key_frame=1 pts=76304 pts_time=1.589667  duration_time=0.021333
    audio stream_index=1 key_frame=1 pts=77328 pts_time=1.611000  duration_time=0.021333

    pts stands for "presentation time stamp" and pts_time is of interest.
    Video-time-intervall: from 1.300000  to  1.600000+0.033333=1.633333
    Audio-time-intervall: from 1.312333  to  1.611000+0.021333=1.632333

    Audio is a bit ahead at the beginning and a tiny bit behind at the end. pts should be multiples of 1024, but they aren't hmm. The difference is still 1024, but they are phase-shifted. Phase-shift is 62992 mod 1024 = 528 (or -496).

     

    The interval from a bit further ahead:

    Video: From 8.066667 to 8.366667+0.033333=8.400000
    Audio: From 8.053667 to 8.352333+0.021333=8.373666  pts-phase-shift: still 528 (-496)

    Audio is lagging behind.

     

    To really see what is happening I will have to implement better statistics than just looking at things 🙂

     

    One further test: I tried to phase-lock audio and video optimally:

    VideoFrameRate: f. AudioFrameRate: 48000/1024, so f = 48000/1024 = 46,875. I've added this frame-rate to the demo.

    Result: Perfect sync for the first audio-video group. In the middle of the second group the pts-phase-shift is again 528, and audio lags behind. For the rest of the groups the lag doesn't get bigger, it is always corrected to some degree. But the file should have identical audio and video timestamps in the first place! 

     

    There is another new frame-rate, which is the result of trying to phase-lock 2 video-frames to 3 audio-frames.  2/f = 3*1024/4800  results in f = 2*48000/3/1024 = 31.25

     

    I will try to find out what causes the phase-shift in audio by parsing the ffprobe-output a bit more (sigh). Maybe generate a log-file for the samples written, too. (Sigh). No, so far it's still fun.:classic_biggrin:

    For those, who made it up to here: Thanks for your patience.

    Renate  

    • Like 2

  20. First of all, thanks. I'll be back as soon as I understand better what the analysis is showing, and then I might be able to do the "little code changes" you mention :). Just keep in mind, that the sinkwriter isn't giving you any control over dts, pts., or how video and audio are interleaved. 

     

    Renate

    1 hour ago, Kas Ob. said:

    Anyway, that is all and good luck, it is your code and it is your call.

     

    • Like 1
×